Overview
Static and dynamic websites represent two fundamentally different approaches to delivering web content. Static websites consist of fixed HTML, CSS, and JavaScript files served directly to users without server-side processing. The content remains the same for every visitor until a developer manually updates the files. Dynamic websites generate content on-demand through server-side code, typically pulling data from databases and assembling pages in response to each request.
The distinction extends beyond simple implementation details to encompass architecture, performance, security, and maintenance patterns. A static site might serve a company homepage where content changes monthly, while a dynamic site powers a social media platform where content updates continuously and personalizes for each user.
Static websites existed first in web history. Early websites consisted entirely of hand-written HTML files uploaded to servers. As the web evolved, dynamic capabilities emerged through CGI scripts, server-side languages, and database integration. This enabled personalization, user authentication, and interactive features. More recently, static sites have experienced renewed interest through static site generators and JAMstack architectures that combine static delivery with dynamic functionality through APIs and JavaScript.
<!-- Static Website: Pre-built HTML file -->
<!DOCTYPE html>
<html>
<head><title>Products</title></head>
<body>
<h1>Our Products</h1>
<ul>
<li>Product A - $29.99</li>
<li>Product B - $39.99</li>
</ul>
</body>
</html>
# Dynamic Website: Ruby generates HTML per request
get '/products' do
@products = Product.all
erb :products
end
# products.erb template
<h1>Our Products</h1>
<ul>
<% @products.each do |product| %>
<li><%= product.name %> - $<%= product.price %></li>
<% end %>
</ul>
Key Principles
The core distinction between static and dynamic websites centers on when and where content generation occurs. Static websites perform all content generation at build time, producing complete HTML files before deployment. Dynamic websites perform content generation at request time, executing code and querying databases when users access pages.
Static Website Characteristics
Static sites consist of pre-generated files stored on a web server or CDN. When a browser requests a page, the server retrieves and sends the corresponding HTML file without modification. No server-side code execution occurs during the request. Changes to content require rebuilding the entire site or affected pages, then deploying the updated files.
The build process converts source content (Markdown, templates, data files) into final HTML, CSS, and JavaScript files. This happens on a developer's machine or in a CI/CD pipeline. The output is a directory structure containing all necessary files, ready for deployment to any static file host.
Dynamic Website Characteristics
Dynamic sites execute server-side code for each request. The application server receives a request, runs Ruby code (or another language), queries databases, processes business logic, and generates HTML. The same URL can return different content based on the current user, time of day, database state, or request parameters.
Database interaction forms a central component. User profiles, blog posts, product inventories, and other content reside in databases. The application queries this data during request processing and injects it into templates. Multiple users viewing the same URL simultaneously might see different content based on their authentication state or personalization settings.
Request Processing Flow
For static sites: Browser requests URL → Web server locates file → Server sends file → Browser renders HTML. Processing time typically measures in single-digit milliseconds.
For dynamic sites: Browser requests URL → Application server receives request → Server executes Ruby code → Code queries database → Database returns data → Code processes data → Template engine generates HTML → Server sends HTML → Browser renders. Processing time ranges from tens to hundreds of milliseconds depending on complexity.
Content Update Mechanisms
Static sites require a build-and-deploy cycle. A developer or content editor updates source files, triggers a build process that regenerates affected pages, and deploys the new files to hosting. The entire site or changed pages become available atomically after deployment completes.
Dynamic sites update content through database modifications. An administrator logs into a CMS, updates a post, saves changes, and the new content appears immediately on the next page request. No deployment or build process occurs.
Hybrid Approaches
Modern architectures blur these boundaries. Server-side rendering (SSR) generates static HTML at request time but caches results. Incremental static regeneration (ISR) rebuilds individual pages on-demand when content changes. Client-side rendering loads minimal HTML then uses JavaScript to fetch data from APIs and build the page in the browser. These approaches combine static delivery speed with dynamic content capabilities.
Design Considerations
Selecting between static and dynamic architectures depends on content update frequency, personalization requirements, scale, team capabilities, and budget constraints.
Content Update Frequency
Static sites excel when content changes infrequently. Marketing pages, documentation, portfolios, and company websites often update daily at most. The build-and-deploy cycle adds minimal friction. A documentation site might rebuild nightly to incorporate the day's changes.
Dynamic sites handle frequent updates without friction. Social networks, e-commerce inventory, news sites, and collaborative platforms require immediate content visibility. A news site publishing articles every hour cannot wait for builds and deploys.
Edge case: High-frequency updates with known timing can work with static sites. A sports site might rebuild when games end, incorporating final scores. Scheduled builds can refresh content every 15 minutes for near-real-time updates without dynamic infrastructure.
Personalization and User-Specific Content
Static sites serve identical content to all visitors. Personalization requires client-side JavaScript fetching data from APIs. A static site cannot show user-specific information in initial HTML without additional architecture.
Dynamic sites personalize every request. User dashboards, recommendation engines, access-controlled content, and shopping carts require server-side knowledge of the requesting user. The application queries user-specific data and generates appropriate HTML.
Workaround: Static sites can implement authentication and personalization through JavaScript and APIs. The initial HTML loads unpersonalized, then JavaScript fetches user data and updates the page. This creates a slower initial experience but maintains static infrastructure benefits.
Scale and Traffic Patterns
Static sites scale effortlessly. CDNs cache files at edge locations worldwide. A static site can handle millions of requests per hour with minimal infrastructure. Costs remain low regardless of traffic spikes. A viral blog post generates the same infrastructure cost as zero traffic.
Dynamic sites scale vertically (larger servers) or horizontally (more servers), both increasing costs. Each request executes code and queries databases. High traffic requires load balancers, application server clusters, database replication, and caching layers. A traffic spike can overwhelm infrastructure or generate massive cloud bills.
Cache effectiveness differs dramatically. Static sites achieve near-100% cache hit rates since content never changes between deploys. Dynamic sites struggle with cache invalidation—determining when cached content becomes stale. User-specific content often cannot be cached at all.
Development Complexity
Static sites require build tooling. Developers configure static site generators, define templates, and create build pipelines. Initial setup involves learning a new tool ecosystem. Ongoing development focuses on templates and content rather than server-side logic.
Dynamic sites require full-stack infrastructure. Developers set up application servers, databases, background job processors, and monitoring. They implement business logic, database migrations, authentication systems, and error handling. Maintenance includes security patches, database backups, and performance optimization.
Team skills influence this decision. A team comfortable with Ruby and databases can build dynamic sites efficiently. A team focused on frontend development and content might prefer static sites with external services (forms, comments, search) handled through third-party APIs.
Database Requirements
Static sites avoid database operational overhead. No database to maintain, backup, monitor, or secure. Content comes from files in version control. Rollbacks involve deploying previous commits.
Dynamic sites depend on database availability and performance. Database outages take the site offline. Backups require careful planning. Schema migrations need coordination with application code. Database optimization becomes crucial at scale.
Exception: Dynamic sites can use databases solely for administrative interfaces while serving pre-generated content to visitors. An article database feeds a static site generator that rebuilds when content changes. This provides editorial convenience without runtime database dependencies.
Cost Structure
Static hosting costs approach zero for low to medium traffic. Services like Netlify, Vercel, and GitHub Pages offer free hosting for static sites. Even paid CDN services cost less than dynamic hosting since no compute resources are consumed per request.
Dynamic hosting requires always-running servers or serverless compute that charges per request. Minimum monthly costs start at $5-10 for simple sites, rising to hundreds or thousands for high-traffic applications. Database hosting adds additional costs.
Content Management
Static sites traditionally require developer involvement for content changes. Editors modify Markdown files in Git repositories and trigger builds. This technical barrier prevents non-developer content updates.
Dynamic sites offer browser-based content management systems. Editors log in, see WYSIWYG interfaces, and publish changes immediately without developer intervention. This reduces bottlenecks and enables non-technical team members to manage content.
Headless CMS solutions bridge this gap for static sites. Editors use a CMS interface that stores content in a database or API. The static site generator fetches content from the CMS during builds. This provides editor-friendly interfaces while maintaining static delivery.
Ruby Implementation
Ruby supports both static and dynamic website development through multiple frameworks and tools. The dynamic web framework ecosystem matured first, with static site generators emerging later as the static approach gained popularity.
Dynamic Sites with Sinatra
Sinatra provides a lightweight framework for dynamic Ruby websites. It defines routes that execute Ruby code when requested.
require 'sinatra'
require 'sqlite3'
configure do
set :db, SQLite3::Database.new('blog.db')
settings.db.results_as_hash = true
end
get '/' do
posts = settings.db.execute('SELECT * FROM posts ORDER BY created_at DESC LIMIT 10')
erb :index, locals: { posts: posts }
end
get '/post/:id' do
post = settings.db.execute('SELECT * FROM posts WHERE id = ?', params[:id]).first
halt 404 unless post
erb :post, locals: { post: post }
end
Every request executes Ruby code, queries the database, and generates HTML through ERB templates. The application runs continuously on a server.
Dynamic Sites with Rails
Rails provides a full-featured framework for complex dynamic websites with conventions for database models, controllers, and views.
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
@posts = Post.published.order(created_at: :desc).limit(10)
end
def show
@post = Post.find(params[:id])
end
end
# app/models/post.rb
class Post < ApplicationRecord
scope :published, -> { where(published: true) }
validates :title, presence: true
validates :content, presence: true
end
# app/views/posts/show.html.erb
<article>
<h1><%= @post.title %></h1>
<time><%= @post.created_at.strftime('%B %d, %Y') %></time>
<div><%= sanitize @post.content %></div>
</article>
Rails includes Active Record for database interaction, Action View for templating, and extensive middleware for authentication, caching, and asset management. Applications grow to include background jobs, API endpoints, and complex business logic.
Static Sites with Jekyll
Jekyll generates static websites from templates and content files. It runs as a build tool, producing HTML files for deployment.
# _config.yml
title: My Blog
description: A static blog
url: https://example.com
# _posts/2025-10-01-first-post.md
---
layout: post
title: "First Post"
date: 2025-10-01
---
Post content in Markdown.
# _layouts/post.html
<!DOCTYPE html>
<html>
<head>
<title>{{ page.title }} - {{ site.title }}</title>
</head>
<body>
<article>
<h1>{{ page.title }}</h1>
<time>{{ page.date | date: "%B %d, %Y" }}</time>
<div>{{ content }}</div>
</article>
</body>
</html>
Run jekyll build to generate the site in the _site directory. Deploy these static files to any web host. Jekyll supports plugins for extended functionality:
# _plugins/reading_time.rb
module Jekyll
module ReadingTimeFilter
def reading_time(input)
words_per_minute = 200
words = input.split.size
minutes = (words / words_per_minute).ceil
"#{minutes} min read"
end
end
end
Liquid::Template.register_filter(Jekyll::ReadingTimeFilter)
# Usage in templates: {{ content | reading_time }}
Static Sites with Middleman
Middleman offers more flexibility than Jekyll with Ruby-based configuration and asset pipeline integration.
# config.rb
set :css_dir, 'stylesheets'
set :js_dir, 'javascripts'
set :images_dir, 'images'
activate :blog do |blog|
blog.prefix = 'blog'
blog.permalink = '{year}/{month}/{title}.html'
end
configure :build do
activate :minify_css
activate :minify_javascript
activate :asset_hash
end
# Custom helper
helpers do
def format_date(date)
date.strftime('%B %d, %Y')
end
end
# source/blog/2025-10-01-post.html.md
---
title: Blog Post
date: 2025-10-01
---
Content here.
# source/layouts/layout.erb
<!DOCTYPE html>
<html>
<head>
<title><%= current_page.data.title || 'My Site' %></title>
<%= stylesheet_link_tag 'site' %>
</head>
<body>
<%= yield %>
<%= javascript_include_tag 'site' %>
</body>
</html>
Middleman excels at sites with custom build requirements, data files, and complex templates. The middleman build command generates production files.
Hybrid Approach: Rails with Static Caching
Rails applications can cache entire pages as static files, serving them like a static site until cache invalidation:
class PostsController < ApplicationController
caches_page :show
def show
@post = Post.find(params[:id])
end
def update
@post = Post.find(params[:id])
if @post.update(post_params)
expire_page action: :show, id: @post.id
redirect_to @post
else
render :edit
end
end
end
First requests generate HTML and save it to public/posts/123.html. Subsequent requests for that URL receive the static file without executing Ruby code. Updates expire cached pages, forcing regeneration on the next request.
API-Backed Static Sites
Static sites can fetch data from dynamic APIs during build time:
# Rakefile for build process
require 'net/http'
require 'json'
require 'erb'
task :build do
# Fetch data from API
uri = URI('https://api.example.com/posts')
response = Net::HTTP.get(uri)
posts = JSON.parse(response)
# Generate HTML from template
template = ERB.new(File.read('templates/index.html.erb'))
html = template.result_with_hash(posts: posts)
# Write to output directory
File.write('public/index.html', html)
end
This pattern fetches live data during builds while serving static HTML to visitors. Scheduled builds keep content fresh without runtime API calls.
Performance Considerations
Performance differences between static and dynamic websites manifest in response time, server load, bandwidth efficiency, and scaling characteristics.
Response Time
Static websites serve files directly from disk or memory. Web servers like Nginx can serve tens of thousands of static files per second from a single machine. Response times typically range from 1-10 milliseconds for the server processing, plus network latency.
Dynamic websites execute code for each request. Even simple Rails or Sinatra routes take 10-50 milliseconds for Ruby execution and template rendering. Database queries add 5-100 milliseconds depending on complexity and database performance. Complex pages with multiple queries can reach 200-500 milliseconds.
# Static site: Pre-generated HTML (1-10ms server time)
# File served directly by Nginx
# Dynamic site: Ruby execution (50-200ms typical)
def show
@post = Post.find(params[:id]) # 10-30ms database query
@comments = @post.comments.includes(:user).limit(50) # 20-50ms with join
@related = Post.where(category: @post.category).limit(5) # 10-20ms
render :show # 10-50ms template
end
CDN edge caching dramatically improves static site performance. Files cached at edge locations worldwide serve in 10-50 milliseconds total including network time. Dynamic sites benefit less from CDN caching since personalized content cannot be cached.
Server Resource Usage
Static file serving consumes minimal CPU and memory. A server handling 10,000 requests per second for static content might use 10% CPU. Memory usage remains constant regardless of traffic.
Dynamic applications consume CPU for code execution and memory for application instances. A Rails application might use 200-500MB of memory per worker process. Under load, CPU usage scales linearly with requests. A server handling 100 dynamic requests per second might reach 80-90% CPU utilization.
Database connections represent a critical resource. Each application worker maintains database connections. Connection pool exhaustion causes requests to queue or fail. Static sites eliminate database connections entirely for visitor-facing traffic.
Caching Strategies
Static sites achieve perfect cache efficiency. Every file can be cached indefinitely at the CDN edge using far-future expires headers. Cache hit rates approach 100%. Only the first request after deployment hits the origin server.
# Nginx configuration for static files
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
Dynamic sites struggle with cache invalidation. Caching user-specific content is impossible. Public content can be cached but requires invalidation when data changes. Fragment caching helps by caching template portions:
# Rails fragment caching
<% cache @post do %>
<h1><%= @post.title %></h1>
<div><%= @post.content %></div>
<% end %>
# Expires automatically when @post updates
Russian doll caching nests cached fragments, invalidating only changed portions:
<% cache @post do %>
<h1><%= @post.title %></h1>
<% cache ['comments', @post] do %>
<% @post.comments.each do |comment| %>
<% cache comment do %>
<p><%= comment.body %></p>
<% end %>
<% end %>
<% end %>
<% end %>
Bandwidth and Cost
Static files compress effectively with gzip or Brotli. HTML, CSS, and JavaScript shrink to 15-30% of original size. CDNs serve compressed files automatically.
Dynamic sites generate uncompressed HTML that requires middleware compression. Rails applications use Rack::Deflater:
# config/application.rb
config.middleware.use Rack::Deflater
CDN costs for static sites remain low since origin requests are rare. Dynamic sites generate more origin traffic since personalized content cannot be cached, increasing bandwidth costs.
Build Time vs. Runtime Performance
Static sites shift work from request time to build time. A complex documentation site might take 5-10 minutes to build but serve pages in milliseconds. Large static sites with thousands of pages can take 30+ minutes to build.
Incremental builds optimize this by regenerating only changed pages:
# Jekyll incremental build
jekyll build --incremental
# Custom build tool with change detection
changed_files = `git diff --name-only HEAD~1`.split("\n")
pages_to_rebuild = changed_files.select { |f| f.end_with?('.md') }
rebuild_pages(pages_to_rebuild)
Dynamic sites avoid build time entirely but pay the performance cost on every request. For frequently changing content, this trade-off favors dynamic generation. For stable content with high traffic, static generation wins.
Security Implications
Static and dynamic websites present different security profiles, with static sites offering a significantly reduced attack surface while dynamic sites require active security measures.
Attack Surface Reduction
Static sites eliminate entire categories of vulnerabilities. No server-side code execution means no code injection, SQL injection, or command injection vulnerabilities. No database means no database breaches or connection string exposure. No user authentication in the static layer eliminates session hijacking and authentication bypass vulnerabilities.
The attack surface consists primarily of the web server configuration and any client-side JavaScript. Misconfigured CORS headers or exposed .git directories represent the main risks. Static files cannot execute malicious payloads on the server.
# Dynamic site vulnerability example
get '/user/:id' do
# SQL injection vulnerability
user = db.execute("SELECT * FROM users WHERE id = #{params[:id]}").first
erb :user, locals: { user: user }
end
# Static site: No SQL, no injection
# Pre-generated HTML files served directly
Injection Attack Prevention
Dynamic sites must defend against multiple injection types. SQL injection exploits occur when user input enters SQL queries without sanitization. Cross-site scripting (XSS) allows attackers to inject malicious JavaScript. Command injection executes system commands through application vulnerabilities.
Ruby frameworks provide protection mechanisms:
# Rails: Parameterized queries prevent SQL injection
User.where('email = ?', params[:email])
User.where(email: params[:email]) # Hash notation also safe
# ERB escaping prevents XSS
<p><%= @user.bio %></p> # Automatically escaped
<p><%== @user.bio %></p> # Dangerous: disables escaping
# Sinatra: Manual escaping required
require 'erb'
<p><%= ERB::Util.html_escape(@user.bio) %></p>
Static sites sidestep these concerns since no user input enters server-side processing during requests. User input only matters during the build process, where it comes from trusted sources like content management systems or version control.
Authentication and Authorization
Dynamic sites implement authentication systems that introduce security complexity. Password storage, session management, token generation, and permission checking create vulnerability opportunities.
# Rails authentication implementation
class SessionsController < ApplicationController
def create
user = User.find_by(email: params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to root_path
else
flash[:error] = 'Invalid credentials'
render :new
end
end
end
# Authorization in controllers
class PostsController < ApplicationController
before_action :require_login
before_action :require_ownership, only: [:edit, :update, :destroy]
def require_ownership
@post = Post.find(params[:id])
redirect_to root_path unless @post.user_id == current_user.id
end
end
Common vulnerabilities include weak password requirements, missing CSRF protection, insecure session storage, and authorization bypass through parameter manipulation. Static sites defer authentication to external services (OAuth providers, Auth0) or implement it client-side through JavaScript.
Content Security Policies
Both static and dynamic sites benefit from Content Security Policy (CSP) headers that restrict resource loading:
# Rails: Setting CSP headers
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' https://cdn.example.com; style-src 'self' 'unsafe-inline'"
end
end
# Static site: Web server configuration (Nginx)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com";
Static sites have simpler CSP configurations since all resources are known at build time. Dynamic sites must account for runtime-generated inline scripts and styles.
Dependency Vulnerabilities
Dynamic applications depend on numerous gems that may contain security vulnerabilities. Rails applications typically require 50-200 gems. Each represents a potential vulnerability.
# Gemfile.lock inspection
bundle audit check --update
# Example vulnerability output
Name: actionpack
Version: 6.1.4
Advisory: CVE-2021-22885
Criticality: High
URL: https://groups.google.com/g/rubyonrails-security/c/...
Regular dependency updates and security monitoring become critical. Static site generators have dependencies but they affect only build-time security, not production runtime.
File Upload Vulnerabilities
Dynamic sites accepting file uploads face validation challenges. Uploaded files might contain malicious code, overwrite system files, or consume excessive storage.
# Unsafe file upload
post '/upload' do
file = params[:file]
File.open("uploads/#{file[:filename]}", 'wb') do |f|
f.write(file[:tempfile].read)
end
end
# Safer implementation
post '/upload' do
file = params[:file]
# Validate file type
unless ['image/jpeg', 'image/png'].include?(file[:type])
halt 400, 'Invalid file type'
end
# Generate safe filename
ext = File.extname(file[:filename])
filename = "#{SecureRandom.uuid}#{ext}"
# Store outside web root
File.open("private/uploads/#{filename}", 'wb') do |f|
f.write(file[:tempfile].read)
end
end
Static sites typically avoid file uploads in the static layer, delegating to separate services or implementing uploads through JavaScript to APIs.
Tools & Ecosystem
The Ruby ecosystem provides diverse tools for both static and dynamic website development, from full-featured frameworks to specialized utilities.
Dynamic Web Frameworks
Rails remains the dominant Ruby web framework. It provides conventions for MVC architecture, Active Record ORM, Asset Pipeline, Action Cable for WebSockets, and Active Storage for file uploads. Rails applications handle millions of requests for companies like GitHub, Shopify, and Airbnb.
# Rails application structure
rails new myapp --database=postgresql
cd myapp
rails generate scaffold Post title:string content:text published:boolean
rails db:migrate
rails server
# Full CRUD operations generated automatically
# Routes, controller, model, views, tests created
Sinatra offers a lightweight alternative for smaller applications. It provides routing and templating without extensive conventions or dependencies.
# Sinatra application
require 'sinatra'
get '/' do
'Hello world'
end
get '/posts/:id' do
@post = Post.find(params[:id])
erb :post
end
# Modular style for larger apps
class MyApp < Sinatra::Base
configure do
set :database, Sequel.connect(ENV['DATABASE_URL'])
end
get '/' do
erb :index
end
end
Hanami provides an alternative to Rails with explicit architecture and faster performance. It emphasizes objects over magic, multiple sub-applications within one codebase, and repository pattern for database access.
Static Site Generators
Jekyll dominates Ruby static site generation. GitHub Pages uses Jekyll, making it the most deployed static site generator. It converts Markdown and Liquid templates into HTML, supports plugins through Ruby gems, and integrates with GitHub's infrastructure.
# Jekyll plugin example: Custom tag
module Jekyll
class RenderPartial < Liquid::Tag
def initialize(tag_name, markup, tokens)
super
@partial_name = markup.strip
end
def render(context)
partial_path = "_includes/#{@partial_name}.html"
partial = File.read(partial_path)
Liquid::Template.parse(partial).render(context)
end
end
end
Liquid::Template.register_tag('render_partial', Jekyll::RenderPartial)
Middleman offers more programmatic control through Ruby configuration files. It supports multiple template engines (ERB, Haml, Slim), data files for dynamic content, and a proxy system for generating pages from data.
# Middleman: Generate pages from data
# data/products.yml
products:
- slug: product-a
name: Product A
price: 29.99
- slug: product-b
name: Product B
price: 39.99
# config.rb
data.products.products.each do |product|
proxy "/products/#{product[:slug]}.html",
"/templates/product.html",
locals: { product: product }
end
Bridgetown emerged as a modernized Jekyll alternative with Webpack integration, component-based architecture, and improved performance. It maintains Jekyll compatibility while adding contemporary features.
Headless CMS Integration
Contentful, Sanity, and Strapi provide headless CMS capabilities that separate content management from presentation. Static site generators fetch content during builds:
# Jekyll plugin to fetch from Contentful
require 'contentful'
module Jekyll
class ContentfulGenerator < Generator
def generate(site)
client = Contentful::Client.new(
access_token: ENV['CONTENTFUL_TOKEN'],
space: ENV['CONTENTFUL_SPACE']
)
entries = client.entries(content_type: 'post')
entries.each do |entry|
site.pages << ContentfulPage.new(site, entry)
end
end
end
class ContentfulPage < Page
def initialize(site, entry)
@site = site
@base = site.source
@dir = 'posts'
@name = "#{entry.slug}.html"
self.process(@name)
self.read_yaml(File.join(base, '_layouts'), 'post.html')
self.data['title'] = entry.title
self.data['content'] = entry.content
end
end
end
Hosting and Deployment
Heroku pioneered platform-as-a-service for Ruby applications. It deploys Rails and Sinatra apps through Git, manages databases and add-ons, and scales automatically.
# Heroku deployment
git push heroku main
heroku run rails db:migrate
heroku ps:scale web=2
# Procfile
web: bundle exec puma -C config/puma.rb
worker: bundle exec sidekiq
Netlify and Vercel excel at static site hosting. They detect static site generators, build automatically on Git commits, provide CDN distribution, and offer serverless functions for dynamic features.
# netlify.toml configuration
[build]
command = "jekyll build"
publish = "_site"
[[plugins]]
package = "@netlify/plugin-cache-jekyll"
Render offers a middle ground with static site hosting and full application hosting on the same platform.
Ruby Gems for Web Development
Rack provides the foundation for Ruby web frameworks. It defines a simple interface between web servers and Ruby applications:
# Basic Rack application
class MyApp
def call(env)
[200, {'Content-Type' => 'text/html'}, ['Hello World']]
end
end
# Middleware example
class Timer
def initialize(app)
@app = app
end
def call(env)
start = Time.now
status, headers, body = @app.call(env)
duration = Time.now - start
puts "Request took #{duration}s"
[status, headers, body]
end
end
use Timer
run MyApp.new
Puma serves as the default web server for Rails. It handles concurrent requests through threads and workers, supports SSL, and includes hot restart capabilities.
Sequel and ROM provide alternatives to Active Record for database access. They offer more explicit query building and reduced magic:
# Sequel example
require 'sequel'
DB = Sequel.connect(ENV['DATABASE_URL'])
posts = DB[:posts].where(published: true).order(:created_at).all
post = DB[:posts].where(id: params[:id]).first
DB[:posts].insert(
title: params[:title],
content: params[:content],
created_at: Time.now
)
Reference
Architecture Comparison
| Characteristic | Static Websites | Dynamic Websites |
|---|---|---|
| Content Generation | Build time | Request time |
| Server Requirements | File server only | Application server + database |
| Response Time | 1-10ms typical | 50-200ms typical |
| Database Dependency | None for serving | Required for serving |
| Scalability | Excellent, CDN-based | Requires horizontal scaling |
| Cost at Scale | Minimal | Increases with traffic |
| Update Mechanism | Rebuild and deploy | Database modification |
| Personalization | Client-side only | Server-side per request |
| Cache Effectiveness | Near 100% | Variable, user-dependent |
Ruby Framework Selection
| Framework | Type | Complexity | Best For |
|---|---|---|---|
| Jekyll | Static | Low | Blogs, documentation, marketing sites |
| Middleman | Static | Medium | Custom static sites with complex builds |
| Bridgetown | Static | Medium | Modern static sites with component architecture |
| Sinatra | Dynamic | Low | APIs, microservices, simple web apps |
| Rails | Dynamic | High | Full-featured applications, SaaS platforms |
| Hanami | Dynamic | Medium | Explicit architecture, multiple sub-apps |
Security Comparison
| Vulnerability Type | Static Site Risk | Dynamic Site Risk |
|---|---|---|
| SQL Injection | None | High without parameterization |
| XSS | Low, client-side only | High without output escaping |
| CSRF | None in static layer | High without token validation |
| Authentication Bypass | N/A | Medium to high |
| File Upload Exploits | None in static layer | High without validation |
| Session Hijacking | None in static layer | Medium without HTTPS and secure cookies |
| Dependency Vulnerabilities | Build-time only | Runtime impact |
| Server-Side Code Execution | None | High without input validation |
Performance Metrics
| Metric | Static Site | Dynamic Site |
|---|---|---|
| Server Processing Time | 1-10ms | 50-500ms |
| Database Query Time | 0ms | 5-100ms per query |
| Memory per Request | <1MB | 10-50MB |
| Concurrent Requests per Server | 10,000+ | 50-500 |
| CDN Cache Hit Rate | 95-100% | 20-80% |
| Time to First Byte | 10-50ms | 100-500ms |
| Build Time | 1-30 minutes | N/A |
Deployment Commands
| Operation | Static (Jekyll) | Dynamic (Rails) |
|---|---|---|
| Local Development | jekyll serve | rails server |
| Production Build | jekyll build | RAILS_ENV=production bundle exec rake assets:precompile |
| Deploy | git push netlify main | git push heroku main |
| Database Migration | N/A | heroku run rails db:migrate |
| View Logs | N/A | heroku logs --tail |
| Scale Application | Automatic via CDN | heroku ps:scale web=3 |
| Environment Variables | netlify env:set KEY value | heroku config:set KEY=value |
Common Patterns
| Pattern | Static Implementation | Dynamic Implementation |
|---|---|---|
| Blog with Comments | Static HTML + JavaScript API calls | Server-side rendering with database |
| User Authentication | OAuth via JavaScript | Session-based with Devise or custom |
| Search Functionality | Lunr.js client-side index | Full-text database search |
| Form Submission | Third-party service or API | Controller action with database insert |
| Real-time Updates | WebSocket connection to API | Action Cable or Server-Sent Events |
| Content Versioning | Git history | Database records with timestamps |
| Multi-language Support | Separate build per language | Dynamic locale switching |
Hybrid Architecture Options
| Approach | Description | Use Case |
|---|---|---|
| Static with API | Static HTML, JavaScript fetches dynamic data | Content-heavy sites with limited dynamic features |
| Incremental Static Regeneration | Rebuild individual pages on-demand | Large sites with occasional content updates |
| Edge Functions | Serverless functions at CDN edge | Personalization without full server infrastructure |
| Static Export from Dynamic | Generate static files from dynamic app | Documentation from database content |
| Static Front, Dynamic Admin | Public site static, admin interface dynamic | Marketing sites with internal content management |