CrackedRuby CrackedRuby

Overview

Server-Side Rendering (SSR) generates HTML content on the server for each incoming request, sending complete pages to the client. The server executes application code, queries databases, processes data, and renders templates into HTML before transmission. This contrasts with client-side rendering where servers send minimal HTML and JavaScript that builds the interface in the browser.

The approach originated with the web itself. Early web applications exclusively used server-side rendering because browsers provided limited JavaScript capabilities. Each page request triggered server processing, template rendering, and HTML generation. As JavaScript engines improved and single-page applications emerged, client-side rendering gained popularity. Modern SSR represents a return to server rendering with contemporary techniques, often combined with client-side interactivity.

SSR operates through a request-response cycle. The client requests a URL. The server receives the request, executes application logic, retrieves required data, renders templates with that data, and returns complete HTML. The browser receives fully-formed content ready for display without additional rendering steps.

# Basic SSR flow in Sinatra
require 'sinatra'
require 'erb'

get '/articles/:id' do
  # Server executes application logic
  @article = Article.find(params[:id])
  @comments = @article.comments.recent
  
  # Server renders template with data
  erb :article
end

The rendered response contains complete HTML structure, visible content, and semantic markup. Search engines receive the same content users see, improving discoverability. Users see content immediately without waiting for JavaScript execution.

Key Principles

Server-side rendering centers on separating rendering responsibility from the client. The server maintains control over HTML generation, executing logic and rendering views before transmission. This separation creates distinct boundaries between server and client concerns.

The rendering process follows a consistent pattern. The server receives a request containing route information and parameters. Application code processes the request, querying databases and executing business logic. Template engines combine data with markup templates. The resulting HTML flows to the client as a complete document. The browser parses and displays the content without additional processing requirements.

Template engines form the core of SSR implementations. These systems combine static markup with dynamic data, evaluating expressions and executing control structures during rendering. Template syntax embeds data access and logic within markup, creating a declarative rendering description.

# ERB template demonstrating SSR principles
<article class="post">
  <h1><%= @article.title %></h1>
  <time><%= @article.published_at.strftime('%Y-%m-%d') %></time>
  
  <div class="content">
    <%= @article.body %>
  </div>
  
  <section class="comments">
    <% @comments.each do |comment| %>
      <div class="comment">
        <strong><%= comment.author %></strong>
        <p><%= comment.text %></p>
      </div>
    <% end %>
  </section>
</article>

State management differs fundamentally from client-side approaches. The server maintains no rendering state between requests. Each request starts fresh, retrieving necessary data and rendering independently. Sessions and cookies provide continuity across requests, but rendering remains stateless. This stateless model simplifies scaling and reduces server memory requirements.

Data flows unidirectionally from server to client during SSR. The server holds authoritative data, processing and transforming it before rendering. Templates consume this data without modification. The rendered output captures a snapshot of application state at request time. Subsequent changes require new requests to reflect updated data.

Caching operates at multiple levels in SSR architectures. Fragment caching stores rendered template portions, reducing database queries and computation. Page caching saves complete responses, serving identical content to multiple requests. Edge caching places content closer to users through content delivery networks. These caching layers reduce server load and improve response times.

The critical rendering path determines initial display speed. SSR delivers complete content in the first response, eliminating additional round trips for rendering data. Browsers display content progressively as HTML arrives, showing above-the-fold content quickly. This contrasts with client-side rendering where content remains invisible until JavaScript execution completes.

Server-rendered applications handle navigation differently than single-page applications. Each navigation triggers a full page load, requesting and rendering complete HTML. The server processes routing, generates appropriate content, and returns new pages. Browsers manage history and state through native mechanisms rather than JavaScript frameworks.

Ruby Implementation

Ruby web frameworks provide comprehensive SSR capabilities through integrated template systems, routing, and response handling. Rails, Sinatra, and Hanami represent different approaches to server-side rendering, each with distinct architectural patterns.

Rails implements SSR through the Action View component. Controllers render templates automatically, selecting views based on controller and action names. The framework supports multiple template engines, with ERB as the default. Rails includes helpers for generating HTML elements, managing assets, and handling security concerns.

# Rails controller with SSR
class ArticlesController < ApplicationController
  def show
    @article = Article.includes(:comments, :author).find(params[:id])
    @related = Article.related_to(@article).limit(5)
    
    # Renders app/views/articles/show.html.erb automatically
    # Explicit rendering:
    # render :show
    # render template: 'articles/detailed'
    # render partial: 'article_content'
  end
  
  def index
    @articles = Article.published.page(params[:page])
    
    respond_to do |format|
      format.html # Renders index.html.erb
      format.json { render json: @articles }
    end
  end
end

Template engines differ in syntax and capabilities. ERB embeds Ruby code within markup using special delimiters. Haml uses indentation and concise syntax. Slim optimizes for minimal characters. Each engine compiles templates to Ruby code executed during rendering.

# ERB syntax
<div class="article">
  <h2><%= article.title %></h2>
  <% if article.featured? %>
    <span class="badge">Featured</span>
  <% end %>
</div>

# Haml equivalent
.article
  %h2= article.title
  - if article.featured?
    %span.badge Featured

# Slim equivalent
.article
  h2 = article.title
  - if article.featured?
    span.badge Featured

Rails layouts provide page structure wrapping individual view content. The layout template includes common elements like headers, navigation, and footers. Views render within the layout's content block, creating complete pages.

# app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= content_for?(:title) ? yield(:title) : "My App" %></title>
    <%= csrf_meta_tags %>
    <%= stylesheet_link_tag 'application' %>
  </head>
  <body>
    <%= render 'shared/header' %>
    
    <main>
      <%= yield %> <!-- View content renders here -->
    </main>
    
    <%= render 'shared/footer' %>
    <%= javascript_include_tag 'application' %>
  </body>
</html>

Partials extract reusable template fragments. These smaller templates render within larger views, promoting code reuse and organization. Rails passes local variables to partials, isolating their scope.

# Rendering a partial
<%= render 'article', article: @article %>
<%= render partial: 'comment', collection: @comments %>

# app/views/articles/_article.html.erb
<article>
  <h3><%= article.title %></h3>
  <p><%= truncate(article.body, length: 200) %></p>
  <%= link_to 'Read more', article_path(article) %>
</article>

Sinatra provides lightweight SSR with explicit control. The framework includes template rendering methods but requires manual template engine configuration. This minimalism suits smaller applications and APIs with occasional SSR needs.

require 'sinatra'
require 'sinatra/reloader' if development?

# Configure template engine
set :erb, layout: :main_layout

get '/users/:id' do
  @user = User.find(params[:id])
  @posts = @user.posts.order(created_at: :desc)
  
  erb :user_profile
end

post '/articles' do
  @article = Article.create(params[:article])
  
  if @article.persisted?
    redirect "/articles/#{@article.id}"
  else
    @errors = @article.errors
    status 422
    erb :new_article
  end
end

Streaming responses improve perceived performance for large pages. The server sends HTML incrementally as it generates content rather than buffering the complete response. Browsers display content progressively, showing early page sections while later portions render.

# Streaming in Rails
class FeedController < ApplicationController
  def index
    @posts = Post.published.includes(:author)
    
    self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
    self.response.headers['Transfer-Encoding'] = 'chunked'
    
    self.response_body = Enumerator.new do |yielder|
      yielder << render_to_string(partial: 'header')
      
      @posts.find_each do |post|
        yielder << render_to_string(partial: 'post', locals: { post: post })
      end
      
      yielder << render_to_string(partial: 'footer')
    end
  end
end

View helpers encapsulate common rendering logic. These methods generate HTML, format data, and handle repetitive tasks. Rails includes extensive built-in helpers for forms, links, assets, and formatting.

module ArticlesHelper
  def article_metadata(article)
    content_tag :div, class: 'metadata' do
      concat content_tag(:time, article.published_at.to_s(:long))
      concat " by "
      concat link_to(article.author.name, user_path(article.author))
      concat " in "
      concat link_to(article.category.name, category_path(article.category))
    end
  end
  
  def reading_time(text)
    words = text.split.size
    minutes = (words / 200.0).ceil
    "#{minutes} min read"
  end
end

Russian Doll caching optimizes rendering performance through nested cache fragments. Each template fragment specifies cache dependencies. When data changes, only affected fragments expire while unchanged content serves from cache.

# Nested caching strategy
<% cache @article do %>
  <article>
    <h1><%= @article.title %></h1>
    
    <% cache [@article, 'body'] do %>
      <div class="content">
        <%= markdown(@article.body) %>
      </div>
    <% end %>
    
    <% cache [@article, @article.comments] do %>
      <section class="comments">
        <% @article.comments.each do |comment| %>
          <% cache comment do %>
            <%= render comment %>
          <% end %>
        <% end %>
      </section>
    <% end %>
  </article>
<% end %>

Practical Examples

Building a blog article page demonstrates complete SSR implementation. The page displays article content, author information, related articles, and comments with nested replies. The controller retrieves all necessary data in a single query set, avoiding N+1 query problems.

class ArticlesController < ApplicationController
  def show
    @article = Article.includes(:author, comments: [:author, :replies])
                     .find(params[:id])
    @related = Article.where(category: @article.category)
                     .where.not(id: @article.id)
                     .limit(3)
    
    @view_count = @article.increment_view_count
  end
end

The view template structures content semantically, using appropriate HTML5 elements. Template logic handles conditional rendering and collection iteration. Helpers format dates and generate links.

# app/views/articles/show.html.erb
<article class="article-detail">
  <header>
    <h1><%= @article.title %></h1>
    <div class="metadata">
      <%= link_to @article.author.name, user_path(@article.author), 
                  class: 'author' %>
      <time datetime="<%= @article.published_at.iso8601 %>">
        <%= @article.published_at.strftime('%B %d, %Y') %>
      </time>
      <span class="views"><%= @view_count %> views</span>
    </div>
  </header>
  
  <% if @article.cover_image.attached? %>
    <%= image_tag @article.cover_image, alt: @article.title, 
                  class: 'cover-image' %>
  <% end %>
  
  <div class="content">
    <%= sanitize @article.rendered_html %>
  </div>
  
  <%= render 'shared/social_share', article: @article %>
  
  <section class="related-articles">
    <h2>Related Articles</h2>
    <%= render partial: 'article_card', collection: @related %>
  </section>
  
  <section class="comments">
    <h2><%= pluralize(@article.comments.size, 'Comment') %></h2>
    <%= render partial: 'comment', collection: @article.comments %>
  </section>
</article>

The comment partial handles nested replies through recursive rendering. Each comment includes author information and optional reply threads. The recursion stops when no replies exist.

# app/views/articles/_comment.html.erb
<div class="comment" id="comment-<%= comment.id %>">
  <div class="comment-header">
    <%= link_to comment.author.name, user_path(comment.author) %>
    <time datetime="<%= comment.created_at.iso8601 %>">
      <%= time_ago_in_words(comment.created_at) %> ago
    </time>
  </div>
  
  <div class="comment-body">
    <%= sanitize comment.text, tags: %w(p br strong em a) %>
  </div>
  
  <% if comment.replies.any? %>
    <div class="replies">
      <%= render partial: 'comment', collection: comment.replies %>
    </div>
  <% end %>
</div>

E-commerce product pages require complex data assembly. The page shows product details, variants, pricing, reviews, and recommendations. The controller aggregates data from multiple models while maintaining response time constraints.

class ProductsController < ApplicationController
  def show
    @product = Product.includes(:images, :variants, :reviews)
                     .find(params[:id])
    
    @variants = @product.variants.available
    @reviews = @product.reviews.approved.recent.limit(10)
    @avg_rating = @product.reviews.average(:rating)
    
    @recommendations = RecommendationService.new(@product)
                                           .similar_products
                                           .limit(6)
    
    @availability = InventoryService.check(@product.id)
  end
end

The product view template presents structured data for search engines and users. JSON-LD markup provides machine-readable product information. Template logic handles variant selection and conditional display.

# app/views/products/show.html.erb
<% content_for :structured_data do %>
  <script type="application/ld+json">
  {
    "@context": "https://schema.org/",
    "@type": "Product",
    "name": "<%= @product.name %>",
    "image": "<%= url_for(@product.primary_image) %>",
    "description": "<%= strip_tags(@product.description) %>",
    "sku": "<%= @product.sku %>",
    "offers": {
      "@type": "Offer",
      "price": "<%= @product.price %>",
      "priceCurrency": "USD",
      "availability": "<%= @availability.schema_status %>"
    },
    "aggregateRating": {
      "@type": "AggregateRating",
      "ratingValue": "<%= @avg_rating %>",
      "reviewCount": "<%= @product.reviews.count %>"
    }
  }
  </script>
<% end %>

<div class="product-detail">
  <div class="product-gallery">
    <%= render 'product_images', product: @product %>
  </div>
  
  <div class="product-info">
    <h1><%= @product.name %></h1>
    
    <div class="rating">
      <%= render 'shared/star_rating', rating: @avg_rating %>
      <span><%= pluralize(@product.reviews.count, 'review') %></span>
    </div>
    
    <div class="price">
      <%= number_to_currency(@product.price) %>
    </div>
    
    <%= form_with model: @cart_item, url: cart_items_path do |f| %>
      <%= f.hidden_field :product_id, value: @product.id %>
      
      <% if @variants.any? %>
        <div class="variants">
          <%= f.collection_select :variant_id, @variants, :id, :name %>
        </div>
      <% end %>
      
      <%= f.number_field :quantity, value: 1, min: 1 %>
      <%= f.submit 'Add to Cart', disabled: !@availability.in_stock? %>
    <% end %>
    
    <div class="description">
      <%= @product.description %>
    </div>
  </div>
</div>

Form rendering and validation feedback require careful SSR implementation. Invalid submissions redisplay forms with error messages and preserved user input. The server maintains validation state and generates appropriate HTML.

class RegistrationsController < ApplicationController
  def new
    @user = User.new
  end
  
  def create
    @user = User.new(user_params)
    
    if @user.save
      session[:user_id] = @user.id
      redirect_to dashboard_path, notice: 'Account created successfully'
    else
      render :new, status: :unprocessable_entity
    end
  end
  
  private
  
  def user_params
    params.require(:user).permit(:email, :password, :password_confirmation, 
                                  :name, :accept_terms)
  end
end

The form template displays validation errors and repopulates fields with submitted values. Rails form helpers automatically bind to model attributes and handle error display.

# app/views/registrations/new.html.erb
<%= form_with model: @user, url: registrations_path do |f| %>
  <% if @user.errors.any? %>
    <div class="error-summary">
      <h3><%= pluralize(@user.errors.count, 'error') %> prevented saving:</h3>
      <ul>
        <% @user.errors.full_messages.each do |message| %>
          <li><%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>
  
  <div class="field">
    <%= f.label :email %>
    <%= f.email_field :email, required: true, 
                      class: @user.errors[:email].any? ? 'error' : '' %>
    <% if @user.errors[:email].any? %>
      <span class="field-error"><%= @user.errors[:email].join(', ') %></span>
    <% end %>
  </div>
  
  <div class="field">
    <%= f.label :password %>
    <%= f.password_field :password, required: true %>
  </div>
  
  <div class="field">
    <%= f.label :password_confirmation %>
    <%= f.password_field :password_confirmation, required: true %>
  </div>
  
  <div class="field checkbox">
    <%= f.check_box :accept_terms, required: true %>
    <%= f.label :accept_terms, 'I accept the terms of service' %>
  </div>
  
  <%= f.submit 'Create Account' %>
<% end %>

Design Considerations

Choosing between server-side rendering and alternatives depends on application requirements, team capabilities, and user expectations. SSR excels when initial page load performance and search engine visibility matter most. Applications targeting users with slower connections or less capable devices benefit from SSR's minimal client-side requirements.

Content-heavy applications favor SSR. News sites, blogs, documentation platforms, and marketing pages contain primarily static content that changes infrequently. SSR delivers this content immediately without JavaScript overhead. Search engines index content directly from HTML, improving discoverability.

Interactive applications with frequent updates challenge SSR architectures. Each interaction potentially requires server round trips, increasing latency. Applications like spreadsheets, drawing tools, or real-time collaboration benefit from client-side rendering where immediate feedback matters more than initial load time.

Hybrid approaches combine SSR and client-side rendering. The server renders initial page content. JavaScript enhances interactivity after page load, handling dynamic updates without full page refreshes. This progressive enhancement provides SSR benefits while supporting rich interactions.

# Hybrid approach in Rails
class DashboardController < ApplicationController
  def show
    @user = current_user
    @summary = DashboardSummary.new(@user)
    
    # SSR renders initial state
    # JavaScript polls for updates
    respond_to do |format|
      format.html # Renders full page
      format.json { render json: @summary } # Updates for client-side
    end
  end
end

Static site generation serves content that rarely changes. The build process renders all pages once, deploying static HTML files. This eliminates runtime rendering costs and maximizes performance. Documentation sites, blogs with infrequent updates, and marketing pages suit static generation.

Server capacity constraints influence rendering decisions. SSR consumes server CPU and memory for each request. High-traffic applications require more server resources than client-side rendering alternatives. Caching mitigates this but adds complexity.

Team expertise affects implementation success. SSR requires server-side development skills and infrastructure knowledge. Teams comfortable with Ruby and web frameworks implement SSR efficiently. Teams focused on JavaScript might prefer client-side frameworks.

SEO requirements often mandate SSR. Search engines execute JavaScript inconsistently. While Google processes client-rendered content, other engines struggle. SSR guarantees search engines receive complete HTML, ensuring proper indexing.

Time-to-first-byte trades off against time-to-interactive. SSR increases server processing time, delaying initial response. However, the response contains complete content, reducing time until users see meaningful content. Client-side rendering sends minimal HTML quickly but requires JavaScript execution before displaying content.

Accessibility considerations favor SSR. Screen readers and assistive technologies handle standard HTML reliably. Client-side rendering can create accessibility challenges when JavaScript generates content dynamically. SSR produces accessible HTML by default.

Caching strategies differ significantly across rendering approaches. SSR caches at multiple levels: page caching, fragment caching, CDN caching. Client-side rendering caches API responses and compiled JavaScript. SSR caching often provides better hit rates for content-focused applications.

Performance Considerations

Server-side rendering performance depends on multiple factors: template complexity, data retrieval efficiency, caching effectiveness, and server capacity. Each request triggers full rendering pipelines, making optimization crucial for responsiveness.

Database queries dominate SSR performance costs. N+1 query patterns where rendering triggers queries for related records create severe bottlenecks. Eager loading retrieves associated records in minimal queries, reducing database round trips.

# Inefficient - N+1 queries
@articles = Article.all # 1 query
@articles.each do |article|
  article.author.name # N queries
  article.comments.count # N queries
end

# Optimized - preloading
@articles = Article.includes(:author, :comments).all # 3 queries
@articles.each do |article|
  article.author.name # No query
  article.comments.count # No query
end

Template compilation converts template syntax to executable Ruby code. Frameworks cache compiled templates, avoiding recompilation on each request. Development mode often recompiles templates for immediate feedback, while production mode caches aggressively.

Fragment caching stores rendered template portions, keying on data dependencies. When underlying data changes, cache keys update automatically, invalidating stale content. This selective caching minimizes rendering work.

# Fragment caching with automatic key generation
<% cache @product do %>
  <div class="product">
    <h3><%= @product.name %></h3>
    <p><%= @product.description %></p>
    
    <% cache [@product, 'price_block'] do %>
      <div class="price"><%= number_to_currency(@product.price) %></div>
    <% end %>
  </div>
<% end %>

Page caching stores complete rendered responses, serving identical content without executing application code. This aggressive caching maximizes performance but requires careful invalidation strategies. Public content without personalization benefits most.

# Page caching configuration
class ArticlesController < ApplicationController
  caches_page :show, :index
  
  def show
    @article = Article.find(params[:id])
  end
end

# Cache invalidation
class Article < ApplicationRecord
  after_update :expire_page_cache
  
  def expire_page_cache
    ActionController::Base.expire_page(
      controller: 'articles',
      action: 'show',
      id: id
    )
  end
end

ETags enable conditional requests, avoiding redundant rendering. The server generates content identifiers based on resource state. Clients include ETags in subsequent requests. The server returns 304 Not Modified when content unchanged, eliminating transfer costs.

class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    
    if stale?(etag: @article, last_modified: @article.updated_at)
      respond_to do |format|
        format.html
      end
    end
  end
end

View helpers introduce performance overhead when complex logic executes repeatedly. Extracting computed values to controller instance variables reduces redundant calculations. Memoization caches method results within request scope.

# Inefficient helper
module ArticlesHelper
  def related_articles(article)
    # Executes query each call
    Article.where(category: article.category)
           .where.not(id: article.id)
           .limit(5)
  end
end

# Optimized in controller
class ArticlesController < ApplicationController
  def show
    @article = Article.find(params[:id])
    @related_articles = Article.where(category: @article.category)
                               .where.not(id: @article.id)
                               .limit(5)
  end
end

Streaming responses improve perceived performance for large pages. The server sends HTML incrementally, allowing progressive rendering. Users see above-the-fold content while lower sections generate. Streaming requires careful template design to maintain valid HTML structure.

Database connection pooling prevents connection exhaustion under load. Each request acquires a connection from the pool, returning it after completion. Pool size balances concurrency with database capacity.

Asset pipeline optimization reduces page weight. Combining stylesheets and JavaScript files minimizes HTTP requests. Minification removes unnecessary characters. Compression reduces transfer sizes. CDN distribution decreases latency.

Monitoring SSR performance requires tracking multiple metrics. Time-to-first-byte measures server processing duration. Template rendering time identifies slow views. Database query time reveals data access bottlenecks. Cache hit rates indicate caching effectiveness.

# Performance monitoring
class ApplicationController < ActionController::Base
  around_action :log_performance
  
  private
  
  def log_performance
    start = Time.current
    yield
    duration = Time.current - start
    
    Rails.logger.info("Request completed in #{duration * 1000}ms")
    Rails.logger.info("View rendering: #{view_runtime}ms")
    Rails.logger.info("DB queries: #{db_runtime}ms")
  end
end

Security Implications

Server-side rendering introduces security considerations distinct from client-side approaches. The server generates HTML containing potentially sensitive data, requiring careful output handling to prevent vulnerabilities.

Cross-site scripting (XSS) attacks inject malicious scripts through user-controlled data. When SSR inserts unsanitized content into HTML, browsers execute embedded scripts. Rails automatically escapes output by default, but developers must understand escaping contexts.

# Vulnerable - raw output
<div><%= raw @comment.text %></div>

# Safe - automatic escaping
<div><%= @comment.text %></div>

# Safe - explicit sanitization
<div><%= sanitize @comment.text, tags: %w(p br strong em) %></div>

JavaScript contexts require special handling. Embedding data in script tags needs JSON encoding, not HTML escaping. Failure to encode properly enables script injection.

# Vulnerable - HTML escaping insufficient
<script>
  var userData = <%= @user.name %>;
</script>

# Safe - JSON encoding
<script>
  var userData = <%= raw @user.to_json %>;
</script>

# Better - dedicated endpoint
<script>
  fetch('/api/user')
    .then(r => r.json())
    .then(data => console.log(data));
</script>

Cross-site request forgery (CSRF) protection prevents unauthorized actions. SSR generates forms including CSRF tokens. The server validates tokens on submission, rejecting requests without valid tokens. Rails includes CSRF protection by default.

# Automatic CSRF token inclusion
<%= form_with model: @post, url: posts_path do |f| %>
  <%= f.text_field :title %>
  <%= f.submit %>
<% end %>

# Generated HTML includes token
<form action="/posts" method="post">
  <input type="hidden" name="authenticity_token" value="...">
  <input type="text" name="post[title]">
  <input type="submit" value="Create Post">
</form>

Server-side rendering can leak sensitive data through overly verbose responses. Rendering complete objects exposes attributes intended for internal use. Explicitly selecting displayed attributes prevents information disclosure.

# Vulnerable - exposes all attributes
<div data-user="<%= @user.to_json %>"></div>

# Safe - explicit attribute selection
<div data-user="<%= @user.slice(:id, :name, :avatar_url).to_json %>"></div>

Template injection occurs when user input controls template content. Dynamically constructing templates from user data enables arbitrary code execution. Never interpolate user content into template paths or names.

# Vulnerable - template injection
def show
  template_name = params[:template] # User controlled
  render template: "articles/#{template_name}"
end

# Safe - whitelist allowed templates
def show
  allowed_templates = %w(standard detailed minimal)
  template = allowed_templates.include?(params[:view]) ? params[:view] : 'standard'
  render template: "articles/#{template}"
end

SQL injection vulnerabilities arise from unsafe query construction. Rails Active Record automatically parameterizes queries, but raw SQL requires careful handling.

# Vulnerable - SQL injection
Article.where("title = '#{params[:title]}'")

# Safe - parameterized query
Article.where("title = ?", params[:title])
Article.where(title: params[:title])

Content Security Policy headers restrict resource loading, mitigating XSS impact. SSR applications configure CSP headers, specifying allowed script sources and inline script restrictions.

# CSP configuration
class ApplicationController < ActionController::Base
  before_action :set_csp_header
  
  private
  
  def set_csp_header
    response.headers['Content-Security-Policy'] = 
      "default-src 'self'; " \
      "script-src 'self' 'nonce-#{request.request_id}'; " \
      "style-src 'self' 'unsafe-inline'; " \
      "img-src 'self' https:;"
  end
end

Session security depends on proper configuration. Secure and HttpOnly flags prevent session theft through network interception and client-side script access. SameSite attributes mitigate CSRF attacks.

# Secure session configuration
Rails.application.config.session_store :cookie_store,
  key: '_app_session',
  secure: Rails.env.production?,
  httponly: true,
  same_site: :lax

Authorization checks prevent unauthorized access to rendered content. Controllers verify user permissions before rendering sensitive data. Missing authorization allows privilege escalation.

class ArticlesController < ApplicationController
  before_action :authenticate_user!
  before_action :authorize_article, only: [:edit, :update, :destroy]
  
  def edit
    @article = Article.find(params[:id])
  end
  
  private
  
  def authorize_article
    article = Article.find(params[:id])
    redirect_to root_path unless article.user == current_user
  end
end

Reference

Template Engine Comparison

Engine Syntax Style Performance Learning Curve Use Case
ERB Embedded Ruby tags Moderate Low Default Rails choice, familiar to Ruby developers
Haml Indentation-based Fast Moderate Concise markup, enforces structure
Slim Minimalist syntax Fastest Moderate Performance-critical applications
Liquid Safe template language Slower Low User-generated templates, sandboxed execution

Rails Rendering Methods

Method Description Use Case
render Renders template or partial Standard view rendering
render partial Renders partial template Reusable view components
render collection Renders partial for each item List rendering with local variables
render template Renders specific template path Cross-controller templates
render action Renders action without executing Form validation errors
render json Renders JSON response API endpoints
render plain Renders plain text Debug output
redirect_to HTTP redirect Navigation after actions

Caching Strategy Selection

Strategy Scope Invalidation Best For
Page caching Complete HTTP response Manual or time-based Completely static pages
Action caching Controller action result Manual or time-based Pages with minimal personalization
Fragment caching Template portions Key-based Partially dynamic content
Russian Doll caching Nested fragments Automatic via keys Complex nested structures
Low-level caching Arbitrary data Manual Computation results

HTTP Response Headers

Header Purpose Example Value
Content-Type Response format text/html; charset=utf-8
Cache-Control Caching directives public, max-age=3600
ETag Content identifier W/"4d7c1b456e8a2f3c"
Last-Modified Resource modification time Wed, 01 Jan 2025 12:00:00 GMT
Content-Security-Policy Security restrictions default-src 'self'
X-Frame-Options Clickjacking prevention SAMEORIGIN
X-Content-Type-Options MIME type enforcement nosniff

Common View Helpers

Helper Purpose Example
link_to Generate hyperlinks link_to 'Home', root_path
image_tag Insert images image_tag 'logo.png'
form_with Create forms form_with model: @user
content_tag Generate HTML tags content_tag :div, 'Text'
number_to_currency Format currency number_to_currency(19.99)
time_ago_in_words Relative time time_ago_in_words(Time.current)
truncate Shorten text truncate(text, length: 100)
sanitize Clean HTML sanitize(html, tags: %w(p))

Performance Optimization Checklist

Technique Impact Implementation Complexity
Eager loading associations High Low
Fragment caching High Medium
Page caching Very High Medium
ETags Medium Low
Asset compression Medium Low
CDN distribution High Medium
Database indexing High Medium
Query optimization High Medium
Connection pooling Medium Low
Template compilation caching Medium Automatic

Security Configuration Options

Setting Purpose Recommended Value
force_ssl HTTPS enforcement true in production
csrf_protection CSRF attack prevention true
session.secure Secure cookie flag true in production
session.httponly HttpOnly cookie flag true
session.same_site SameSite cookie attribute :lax or :strict
sanitize_tags Allowed HTML tags Whitelist only necessary tags
sanitize_attributes Allowed HTML attributes Whitelist only necessary attributes

Template Syntax Quick Reference

ERB Output Tags

<%= expression %>    - Escaped output
<%== expression %>   - Unescaped output
<% code %>          - Non-output code
<%# comment %>      - Template comment

Common Template Patterns

# Conditional rendering
<% if condition %>
  <div>Content</div>
<% end %>

# Collection iteration
<% @items.each do |item| %>
  <%= item.name %>
<% end %>

# Partial rendering
<%= render 'shared/header' %>
<%= render partial: 'item', collection: @items %>

# Content blocks
<% content_for :sidebar do %>
  <div>Sidebar content</div>
<% end %>

# Yielding content
<%= yield :sidebar %>