Overview
Search Engine Optimization (SEO) encompasses the technical and content strategies that affect how search engines crawl, index, and rank web pages. Search engines use automated bots to discover pages, parse their content and structure, and determine relevance for user queries. The technical implementation of SEO affects whether search engines can access pages, understand their content, and present them effectively in search results.
SEO divides into three primary domains: technical SEO addresses server configuration, page structure, and crawlability; on-page SEO focuses on content optimization and HTML markup; off-page SEO involves external factors like backlinks. This documentation addresses technical and on-page elements that developers control through code and configuration.
Search engines rank pages through algorithms that evaluate hundreds of signals. Core ranking factors include content relevance, page authority, user experience metrics, mobile usability, and page speed. Technical implementation directly impacts many of these factors. A page with excellent content remains invisible if search engine bots cannot crawl it, parse its structure, or load it quickly enough for acceptable user experience.
# Basic meta tags in Rails ERB template
<head>
<title><%= content_for?(:title) ? yield(:title) : "Default Site Title" %></title>
<meta name="description" content="<%= content_for?(:description) ? yield(:description) : "Default description" %>">
<meta name="robots" content="index, follow">
<link rel="canonical" href="<%= request.original_url %>">
</head>
The impact of proper SEO implementation manifests in organic search traffic, which often represents 50-70% of total website traffic for content-driven sites. Technical SEO issues can prevent entire sections of a site from appearing in search results, while proper implementation enables search engines to understand page purpose, content hierarchy, and relationships between pages.
Key Principles
Crawlability determines whether search engine bots can discover and access pages. Bots follow links from known pages to discover new content, respect robots.txt directives, and maintain crawl budgets that limit how many pages they process per site. Sites must provide clear navigation paths, generate XML sitemaps, and avoid blocking important content through robots.txt or authentication requirements.
Indexability controls whether discovered pages enter the search engine's index. Pages marked with noindex meta tags, blocked by robots.txt, or containing duplicate content may not be indexed. Canonical tags indicate the preferred version when multiple URLs contain similar content. Search engines maintain separate indices for different device types, with mobile-first indexing becoming the primary approach.
Content Structure communicates page meaning through semantic HTML elements. Header tags (h1-h6) establish content hierarchy, with h1 typically identifying the primary topic. Search engines parse HTML structure to understand topic relationships and content organization. Proper semantic markup helps algorithms determine page purpose and relevant search queries.
Metadata provides explicit information about page content through meta tags in the document head. The title tag appears in search results and browser tabs, carrying significant ranking weight. Meta descriptions summarize page content for search result snippets, though search engines may generate alternative descriptions. Robots meta tags control indexing and link-following behavior.
URL Structure affects both crawlability and user experience. Clean, descriptive URLs perform better than opaque parameter strings. URL paths should reflect site hierarchy and content organization. Search engines interpret hyphens as word separators but treat underscores as word joiners. URLs persist as permanent identifiers, making careful initial structure critical.
Page Speed directly impacts rankings and user experience metrics. Search engines measure Core Web Vitals: Largest Contentful Paint (LCP) for loading performance, First Input Delay (FID) for interactivity, and Cumulative Layout Shift (CLS) for visual stability. Slow-loading pages rank lower and experience higher bounce rates.
Mobile Optimization determines rankings in mobile search results. Responsive design ensures content displays correctly across device sizes. Touch targets must meet minimum size requirements. Text remains readable without zooming. Mobile-first indexing means Google primarily uses mobile page versions for ranking and indexing.
Structured Data provides explicit context about page content through schema.org markup. JSON-LD format embeds structured data in script tags without affecting page rendering. Rich results like review stars, recipe cards, and event information derive from structured data markup.
# Schema.org structured data in Rails
def article_structured_data(article)
{
"@context": "https://schema.org",
"@type": "Article",
"headline": article.title,
"datePublished": article.published_at.iso8601,
"dateModified": article.updated_at.iso8601,
"author": {
"@type": "Person",
"name": article.author.name
},
"publisher": {
"@type": "Organization",
"name": "Site Name",
"logo": {
"@type": "ImageObject",
"url": "https://example.com/logo.png"
}
}
}.to_json
end
Link Architecture distributes authority throughout a site through internal linking. Links pass "link equity" (commonly called PageRank) from one page to another. Strategic internal linking helps important pages rank higher. Anchor text describes link destinations, helping search engines understand target page topics. External links to authoritative sources can enhance page credibility.
Duplicate Content occurs when identical or very similar content appears at multiple URLs. Search engines filter duplicate content from results, potentially suppressing the preferred version. Canonical tags indicate the primary URL. Parameters and session IDs often create duplicate content issues requiring URL parameter handling in Search Console.
Ruby Implementation
Rails provides several mechanisms for implementing SEO best practices through helpers, gems, and framework conventions. The asset pipeline, view helpers, and routing system all affect SEO implementation.
Meta Tag Management
Rails view helpers generate SEO meta tags dynamically. The content_for mechanism allows views to set page-specific titles and descriptions that layouts insert into the document head.
# app/helpers/application_helper.rb
module ApplicationHelper
def meta_title(title = nil)
if title.present?
content_for :title, title
else
content_for?(:title) ? yield(:title) : "Default Site Title"
end
end
def meta_description(description = nil)
if description.present?
content_for :description, description
else
content_for?(:description) ? yield(:description) : "Default description"
end
end
def canonical_url(url = nil)
url || request.original_url
end
end
# app/views/layouts/application.html.erb
<head>
<title><%= meta_title %></title>
<meta name="description" content="<%= meta_description %>">
<link rel="canonical" href="<%= canonical_url %>">
</head>
# app/views/articles/show.html.erb
<% content_for :title, @article.title %>
<% content_for :description, @article.summary %>
Sitemap Generation
XML sitemaps help search engines discover pages and understand update frequencies. The sitemap_generator gem creates and maintains sitemaps automatically.
# Gemfile
gem 'sitemap_generator'
# config/sitemap.rb
SitemapGenerator::Sitemap.default_host = 'https://example.com'
SitemapGenerator::Sitemap.create do
add root_path, priority: 1.0, changefreq: 'daily'
Article.published.find_each do |article|
add article_path(article),
lastmod: article.updated_at,
changefreq: 'weekly',
priority: 0.8
end
Category.find_each do |category|
add category_path(category),
changefreq: 'weekly',
priority: 0.6
end
end
# Rake task to generate sitemap
# rake sitemap:refresh
Robots.txt Configuration
Rails serves robots.txt from the public directory, but dynamic generation allows environment-specific rules.
# config/routes.rb
get '/robots.txt', to: 'robots#index', defaults: { format: 'text' }
# app/controllers/robots_controller.rb
class RobotsController < ApplicationController
def index
respond_to :text
if Rails.env.production?
render plain: production_robots
else
render plain: staging_robots
end
end
private
def production_robots
<<~ROBOTS
User-agent: *
Disallow: /admin/
Disallow: /api/
Allow: /
Sitemap: #{root_url}sitemap.xml.gz
ROBOTS
end
def staging_robots
<<~ROBOTS
User-agent: *
Disallow: /
ROBOTS
end
end
URL Structure and Routing
Rails routing conventions support SEO-friendly URLs. The friendly_id gem extends this by generating human-readable slugs from model attributes.
# Gemfile
gem 'friendly_id'
# app/models/article.rb
class Article < ApplicationRecord
extend FriendlyId
friendly_id :title, use: [:slugged, :history]
def should_generate_new_friendly_id?
title_changed? || slug.blank?
end
end
# Migration
class AddSlugToArticles < ActiveRecord::Migration[7.0]
def change
add_column :articles, :slug, :string
add_index :articles, :slug, unique: true
end
end
# Controller finds by slug automatically
# GET /articles/introduction-to-ruby-programming
def show
@article = Article.friendly.find(params[:id])
end
Canonical URL Handling
Rails applications often serve the same content through multiple URLs (with and without trailing slashes, different parameters). Canonical tags prevent duplicate content issues.
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_canonical_url
private
def set_canonical_url
@canonical_url = request.base_url + request.path
end
end
# app/helpers/application_helper.rb
def canonical_tag
tag.link rel: 'canonical', href: @canonical_url if @canonical_url
end
Structured Data Implementation
Rails provides JSON serialization for structured data markup. Helper methods generate schema.org JSON-LD for different content types.
# app/helpers/structured_data_helper.rb
module StructuredDataHelper
def article_schema(article)
{
"@context": "https://schema.org",
"@type": "Article",
"headline": article.title,
"description": article.summary,
"image": article.image_url,
"datePublished": article.published_at.iso8601,
"dateModified": article.updated_at.iso8601,
"author": {
"@type": "Person",
"name": article.author.name,
"url": author_url(article.author)
},
"publisher": publisher_schema
}
end
def breadcrumb_schema(breadcrumbs)
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": breadcrumbs.map.with_index do |crumb, index|
{
"@type": "ListItem",
"position": index + 1,
"name": crumb[:name],
"item": crumb[:url]
}
end
}
end
def structured_data_tag(data)
tag.script data.to_json.html_safe, type: 'application/ld+json'
end
end
# app/views/articles/show.html.erb
<%= structured_data_tag(article_schema(@article)) %>
Open Graph and Social Meta Tags
Social media platforms use Open Graph tags to generate rich previews. Rails helpers generate these tags from model attributes.
# app/helpers/social_helper.rb
module SocialHelper
def og_tags(title: nil, description: nil, image: nil, url: nil)
tags = {
'og:title' => title || meta_title,
'og:description' => description || meta_description,
'og:image' => image || default_og_image,
'og:url' => url || request.original_url,
'og:type' => 'website',
'og:site_name' => 'Site Name'
}
safe_join(tags.map { |property, content|
tag.meta property: property, content: content
})
end
def twitter_card_tags(card_type: 'summary_large_image')
tag.meta(name: 'twitter:card', content: card_type) +
tag.meta(name: 'twitter:site', content: '@username')
end
end
Practical Examples
Article Publishing Platform
An article publishing system requires comprehensive SEO implementation across templates, models, and controllers.
# app/models/article.rb
class Article < ApplicationRecord
extend FriendlyId
friendly_id :title, use: [:slugged, :history]
belongs_to :category
belongs_to :author
validates :title, presence: true, length: { maximum: 60 }
validates :meta_description, length: { maximum: 160 }
scope :published, -> { where(published: true).where('published_at <= ?', Time.current) }
scope :by_recency, -> { order(published_at: :desc) }
def seo_title
meta_title.presence || title
end
def seo_description
meta_description.presence || summary.truncate(160)
end
end
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def show
@article = Article.published.friendly.find(params[:id])
@breadcrumbs = build_breadcrumbs(@article)
@canonical_url = article_url(@article)
fresh_when(@article, public: true)
end
private
def build_breadcrumbs(article)
[
{ name: 'Home', url: root_url },
{ name: article.category.name, url: category_url(article.category) },
{ name: article.title, url: article_url(article) }
]
end
end
# app/views/articles/show.html.erb
<% content_for :title, @article.seo_title %>
<% content_for :description, @article.seo_description %>
<%= structured_data_tag(article_schema(@article)) %>
<%= structured_data_tag(breadcrumb_schema(@breadcrumbs)) %>
<%= og_tags(
title: @article.title,
description: @article.summary,
image: @article.featured_image_url
) %>
<article itemscope itemtype="https://schema.org/Article">
<h1 itemprop="headline"><%= @article.title %></h1>
<time itemprop="datePublished" datetime="<%= @article.published_at.iso8601 %>">
<%= @article.published_at.strftime('%B %d, %Y') %>
</time>
<div itemprop="articleBody">
<%= @article.rendered_content %>
</div>
</article>
E-commerce Product Pages
Product pages require specialized structured data and careful handling of variant URLs to avoid duplicate content.
# app/models/product.rb
class Product < ApplicationRecord
extend FriendlyId
friendly_id :name, use: [:slugged, :history]
has_many :variants
has_many :images
belongs_to :category
def primary_image
images.order(:position).first
end
def structured_data
{
"@context": "https://schema.org",
"@type": "Product",
"name": name,
"description": description,
"image": images.map(&:url),
"sku": sku,
"brand": {
"@type": "Brand",
"name": brand_name
},
"offers": offers_data
}
end
private
def offers_data
if variants.any?
variants.map { |v| variant_offer(v) }
else
single_offer
end
end
def variant_offer(variant)
{
"@type": "Offer",
"url": product_url(self),
"priceCurrency": "USD",
"price": variant.price.to_f,
"availability": variant.in_stock? ? "https://schema.org/InStock" : "https://schema.org/OutOfStock",
"sku": variant.sku
}
end
end
# app/controllers/products_controller.rb
class ProductsController < ApplicationController
def show
@product = Product.friendly.find(params[:id])
# Redirect variant-specific URLs to canonical product page
if params[:variant_id]
redirect_to product_path(@product), status: :moved_permanently
return
end
@canonical_url = product_url(@product)
set_cache_headers
end
private
def set_cache_headers
expires_in 1.hour, public: true
fresh_when(@product)
end
end
Multi-language Content
International sites require hreflang tags to indicate language and regional variations.
# app/models/page.rb
class Page < ApplicationRecord
has_many :translations, class_name: 'PageTranslation'
def translation_for(locale)
translations.find_by(locale: locale)
end
def available_locales
translations.pluck(:locale)
end
end
# app/helpers/i18n_helper.rb
module I18nHelper
def hreflang_tags(page)
tags = page.available_locales.map do |locale|
tag.link(
rel: 'alternate',
hreflang: locale,
href: page_url(page, locale: locale)
)
end
# x-default for default language
tags << tag.link(
rel: 'alternate',
hreflang: 'x-default',
href: page_url(page, locale: I18n.default_locale)
)
safe_join(tags)
end
end
# app/views/layouts/application.html.erb
<head>
<%= hreflang_tags(@page) if @page %>
<link rel="canonical" href="<%= page_url(@page, locale: I18n.locale) %>">
</head>
Blog with Category Archives
Category and archive pages require careful pagination and canonical URL handling to avoid duplicate content from page parameters.
# app/controllers/categories_controller.rb
class CategoriesController < ApplicationController
def show
@category = Category.friendly.find(params[:id])
@page = params[:page]&.to_i || 1
@articles = @category.articles.published.by_recency.page(@page).per(20)
# Canonical URL points to page 1
@canonical_url = if @page == 1
category_url(@category)
else
category_url(@category, page: @page)
end
# Pagination rel tags
@prev_page_url = category_url(@category, page: @page - 1) if @page > 1
@next_page_url = category_url(@category, page: @page + 1) if @articles.next_page
set_noindex_for_deep_pages
end
private
def set_noindex_for_deep_pages
@noindex = @page > 10 # Prevent deep pagination pages from indexing
end
end
# app/views/categories/show.html.erb
<% if @noindex %>
<meta name="robots" content="noindex, follow">
<% end %>
<%= tag.link(rel: 'prev', href: @prev_page_url) if @prev_page_url %>
<%= tag.link(rel: 'next', href: @next_page_url) if @next_page_url %>
<h1><%= @category.name %></h1>
<%= render @articles %>
<%= paginate @articles %>
Implementation Approaches
Technical SEO Foundation
Establish server-level configurations before implementing page-level optimizations. Configure HTTPS across the entire site, as search engines prefer secure connections and browsers flag non-HTTPS sites. Set up proper redirects for the non-www to www version (or vice versa) to consolidate authority at a single domain. Implement 301 redirects for moved or deleted pages rather than serving 404 errors for pages with inbound links.
Configure robots.txt to block access to administrative areas, API endpoints, and duplicate content paths. Allow access to CSS and JavaScript files that search engines need to render pages correctly. Generate and submit XML sitemaps to search engines through their webmaster tools. Update sitemaps when content changes and monitor indexing status through Search Console.
Content Optimization Strategy
Structure content with clear hierarchy using semantic HTML5 elements. Use a single h1 per page that summarizes the primary topic. Nest h2-h6 tags to establish content relationships. Include descriptive alt attributes on images for accessibility and image search. Use descriptive link anchor text that indicates destination content rather than generic "click here" text.
Optimize title tags to 50-60 characters with primary keywords near the beginning. Write unique titles for each page that accurately describe content. Craft meta descriptions to 150-160 characters that encourage clicks from search results. Include a call-to-action and primary keywords while maintaining natural language. Update descriptions periodically based on click-through rate data from Search Console.
Performance Optimization
Implement caching strategies at multiple levels: browser caching through Cache-Control headers, CDN caching for static assets, and application-level caching for database queries. Minify CSS and JavaScript, combine files when appropriate, and load non-critical resources asynchronously. Optimize images through compression, appropriate format selection (WebP for modern browsers with fallbacks), and responsive image techniques using srcset attributes.
Measure Core Web Vitals using Lighthouse, PageSpeed Insights, and Chrome User Experience Report. Address LCP issues by optimizing server response time and eliminating render-blocking resources. Reduce FID through code splitting and deferring non-essential JavaScript. Minimize CLS by reserving space for images and ads with explicit width and height attributes.
# config/environments/production.rb
config.public_file_server.enabled = true
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=31536000',
'Expires' => 1.year.from_now.httpdate
}
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_cache_headers
private
def set_cache_headers
expires_in 1.hour, public: true if request.get?
end
end
Mobile-First Development
Design responsive layouts that work across device sizes using flexible grids and media queries. Test on actual devices rather than relying solely on browser developer tools. Ensure touch targets meet minimum size requirements (48x48 CSS pixels) with adequate spacing. Make text readable without zooming by using appropriate font sizes (16px minimum for body text).
Implement viewport meta tags correctly to prevent unwanted zooming behavior. Test forms on mobile devices to ensure input types trigger appropriate keyboards. Optimize tap targets by increasing clickable areas around small elements. Use CSS transforms for animations rather than properties that trigger layout recalculation.
Structured Data Implementation
Choose appropriate schema types based on content: Article for blog posts, Product for e-commerce items, LocalBusiness for physical locations, Event for upcoming activities. Implement required properties for each schema type and include recommended properties when data exists. Test structured data using Google's Rich Results Test before deployment.
Use JSON-LD format placed in the document head for cleaner separation between markup and structured data. Avoid mixing microdata, RDFa, and JSON-LD formats on the same page. Include only truthful, verifiable information in structured data. Update structured data when underlying content changes to maintain accuracy.
Tools & Ecosystem
Analysis and Monitoring
Google Search Console provides crawl error reports, indexing status, mobile usability issues, and Core Web Vitals data. Submit sitemaps, inspect individual URLs, and monitor search appearance features through the interface. Search Console reveals which queries drive traffic and how pages perform in search results.
Google Analytics tracks organic search traffic, landing pages, and user behavior metrics. Set up goals to measure conversion rates from organic traffic. Create segments to analyze search traffic separately from other sources. Monitor bounce rates and time-on-page metrics to identify content improvements.
Lighthouse generates performance, accessibility, and SEO audits directly in Chrome DevTools. Run audits in incognito mode to avoid extension interference. Focus on opportunities section for highest-impact improvements. Compare scores over time to track optimization progress.
Ruby Gems
meta-tags gem provides DSL for managing meta tags with sensible defaults and OpenGraph support.
# Gemfile
gem 'meta-tags'
# app/views/layouts/application.html.erb
<%= display_meta_tags site: 'Site Name',
reverse: true,
separator: '|' %>
# app/controllers/articles_controller.rb
def show
@article = Article.find(params[:id])
set_meta_tags title: @article.title,
description: @article.summary,
keywords: @article.tags.pluck(:name),
og: {
title: @article.title,
description: @article.summary,
image: @article.image_url
}
end
sitemap_generator gem creates and maintains XML sitemaps with automatic compression and cloud storage integration.
# config/sitemap.rb
SitemapGenerator::Sitemap.default_host = 'https://example.com'
SitemapGenerator::Sitemap.sitemaps_path = 'sitemaps/'
SitemapGenerator::Sitemap.create do
add root_path, priority: 1.0, changefreq: 'daily'
Article.published.find_each do |article|
add article_path(article), lastmod: article.updated_at
end
end
friendly_id generates human-readable slugs from model attributes with history tracking and finders.
rack-cache provides HTTP caching middleware that respects Cache-Control headers.
rack-attack rate-limits requests to prevent crawl budget waste from aggressive bots while allowing legitimate search engine crawlers.
Chrome Extensions
SEO Meta in 1 Click displays all meta tags, headings, and images on the current page in an organized panel. The extension highlights missing or problematic tags and provides quick access to important SEO elements.
Lighthouse generates comprehensive performance and SEO audits with specific recommendations. The extension runs multiple audits to account for variability in performance measurements.
Redirect Path shows HTTP status codes and redirect chains. The extension identifies redirect loops, excessive redirect chains, and missing redirects that should exist.
Command Line Tools
curl inspects HTTP headers and response codes for specific URLs. Use verbose mode to see complete request/response cycles including redirects.
# Check response headers
curl -I https://example.com
# Follow redirects and show final destination
curl -L -I https://example.com
# Check robots.txt
curl https://example.com/robots.txt
Lighthouse CLI provides automated auditing in CI/CD pipelines and generates detailed JSON reports for analysis.
npm install -g lighthouse
# Run audit and save report
lighthouse https://example.com --output=json --output-path=./report.json
# Run with specific presets
lighthouse https://example.com --preset=desktop
Common Pitfalls
Blocking Resources in Robots.txt
Blocking CSS and JavaScript files in robots.txt prevents search engines from rendering pages correctly. Modern search engines execute JavaScript to understand page content, requiring access to all rendering resources. Block only truly private content like administrative interfaces.
# Bad robots.txt
User-agent: *
Disallow: /assets/ # Blocks CSS/JS
Disallow: /admin/
# Good robots.txt
User-agent: *
Disallow: /admin/
Allow: /assets/
Sitemap: https://example.com/sitemap.xml.gz
Duplicate Content from URL Parameters
Session IDs, tracking parameters, and sorting options create duplicate content when the same page appears at multiple URLs. Use canonical tags to indicate the preferred version or configure URL parameters in Search Console.
# app/controllers/products_controller.rb
def index
@products = Product.all
@products = @products.where(category: params[:category]) if params[:category]
@products = @products.order(params[:sort]) if params[:sort]
# Always use base URL as canonical
@canonical_url = products_url
end
Missing or Duplicate Title Tags
Forgetting to set page-specific titles causes all pages to share the site-wide default title. Each page requires a unique, descriptive title. Dynamically generating titles from content prevents duplication.
# Bad - same title everywhere
<title>My Website</title>
# Good - dynamic per-page titles
<title><%= content_for?(:title) ? yield(:title) : "Default Title" %></title>
Redirect Chains and Loops
Multiple redirects in sequence slow page loading and waste crawl budget. Each redirect adds latency and increases the chance of errors. Redirect directly to the final destination rather than creating chains.
# Bad - redirect chain
/old-url -> /newer-url -> /final-url
# Good - direct redirect
/old-url -> /final-url
# app/config/routes.rb
# Consolidate redirects
get '/old-url', to: redirect('/final-url', status: 301)
Unoptimized Images
Large image files slow page loading and harm Core Web Vitals scores. Compress images, use appropriate formats, and implement responsive images. Include explicit width and height attributes to prevent layout shift.
# app/helpers/image_helper.rb
def responsive_image_tag(source, options = {})
image_tag source, {
loading: 'lazy',
width: options[:width],
height: options[:height],
alt: options[:alt] || '',
srcset: generate_srcset(source)
}
end
def generate_srcset(source)
[1, 2].map { |size| "#{source_at_size(source, size)} #{size}x" }.join(', ')
end
Incorrect Canonical Tag Usage
Setting canonical tags to incorrect URLs removes pages from search results. Self-referencing canonicals should point to the current page's clean URL. Cross-domain canonicals require careful consideration and are rarely appropriate.
# Bad - canonical points elsewhere
<link rel="canonical" href="https://other-domain.com/page">
# Good - self-referencing canonical
<link rel="canonical" href="https://example.com/page">
Flash of Unstyled Content
Pages that rely heavily on JavaScript for content rendering may show nothing to search engines if JavaScript fails or executes slowly. Implement server-side rendering or ensure critical content appears in initial HTML.
# app/views/articles/show.html.erb
# Bad - content only in JavaScript
<div id="article-content"></div>
<script>
fetchArticle(<%= @article.id %>).then(renderContent);
</script>
# Good - content in HTML
<article>
<h1><%= @article.title %></h1>
<%= @article.content %>
</article>
Ignoring Mobile Usability
Sites that work well on desktop but fail mobile usability tests rank lower in mobile search results. Test touch interactions, font sizes, and viewport configuration on actual mobile devices.
Missing Alt Attributes
Images without alt attributes fail accessibility guidelines and miss image search opportunities. Write descriptive alt text that conveys image content and context. Leave alt empty for purely decorative images.
# Bad
<%= image_tag 'product.jpg' %>
# Good
<%= image_tag 'product.jpg', alt: "Red leather wallet with brass zipper" %>
Broken Internal Links
Links to non-existent pages create poor user experience and waste crawl budget. Monitor for 404 errors and fix broken links promptly. Implement proper redirects when moving or deleting content.
Reference
Essential Meta Tags
| Tag | Purpose | Optimal Length |
|---|---|---|
| title | Page title in search results and browser tabs | 50-60 characters |
| meta description | Summary text in search results | 150-160 characters |
| meta robots | Control indexing and link following | N/A |
| link canonical | Indicate preferred URL for duplicate content | N/A |
| meta viewport | Control mobile rendering | N/A |
Robots Meta Tag Values
| Value | Effect |
|---|---|
| index | Allow page in search index |
| noindex | Exclude page from search index |
| follow | Follow links on page |
| nofollow | Do not follow links on page |
| noarchive | Prevent cached version in search results |
| nosnippet | Prevent snippet in search results |
HTTP Status Codes for SEO
| Code | Meaning | SEO Impact |
|---|---|---|
| 200 | OK | Normal indexing |
| 301 | Permanent redirect | Passes link equity to new URL |
| 302 | Temporary redirect | Does not pass link equity |
| 404 | Not found | Page removed from index |
| 410 | Gone permanently | Faster removal than 404 |
| 503 | Service unavailable | Temporary, maintains index |
Schema.org Common Types
| Type | Use Case | Required Properties |
|---|---|---|
| Article | Blog posts, news articles | headline, image, datePublished |
| Product | E-commerce items | name, image, offers |
| LocalBusiness | Physical locations | name, address, telephone |
| Event | Scheduled events | name, startDate, location |
| Recipe | Cooking instructions | name, recipeIngredient, recipeInstructions |
| Organization | Company information | name, url, logo |
| Person | Individual profiles | name |
Core Web Vitals Thresholds
| Metric | Good | Needs Improvement | Poor |
|---|---|---|---|
| Largest Contentful Paint (LCP) | ≤ 2.5s | 2.5s - 4.0s | > 4.0s |
| First Input Delay (FID) | ≤ 100ms | 100ms - 300ms | > 300ms |
| Cumulative Layout Shift (CLS) | ≤ 0.1 | 0.1 - 0.25 | > 0.25 |
Sitemap XML Attributes
| Attribute | Purpose | Valid Values |
|---|---|---|
| loc | URL of page | Full URL |
| lastmod | Last modification date | ISO 8601 date |
| changefreq | Expected update frequency | always, hourly, daily, weekly, monthly, yearly, never |
| priority | Relative importance | 0.0 to 1.0 |
Common Structured Data Mistakes
| Issue | Problem | Solution |
|---|---|---|
| Missing required properties | Rich results do not appear | Include all required fields per schema |
| Mismatched content | Structured data differs from visible content | Ensure structured data reflects actual page content |
| Wrong schema type | Inappropriate schema for content | Select schema matching content type |
| Invalid JSON | Parsing errors | Validate with structured data testing tool |
Hreflang Language Codes
| Code | Language | Region |
|---|---|---|
| en | English | Generic |
| en-US | English | United States |
| en-GB | English | United Kingdom |
| es | Spanish | Generic |
| es-MX | Spanish | Mexico |
| fr | French | Generic |
| x-default | Default | Language selector page |
Rails SEO Helper Methods
| Method | Purpose | Usage |
|---|---|---|
| content_for | Store content for layout | content_for :title, "Page Title" |
| yield | Retrieve stored content | yield :title |
| link_to | Generate links with anchor text | link_to "Articles", articles_path |
| image_tag | Generate images with alt text | image_tag "photo.jpg", alt: "Description" |
| url_for | Generate absolute URLs | url_for(controller: 'articles', only_path: false) |
Redirect Status Code Selection
| Scenario | Status Code | Rationale |
|---|---|---|
| Permanent URL change | 301 | Transfers link equity, updates index |
| Content moved to new domain | 301 | Consolidates authority at new location |
| Temporary maintenance | 302 or 503 | Preserves original URL in index |
| A/B testing | 302 | Temporary variation |
| Mobile redirect | 302 | User-agent based, not permanent |
| HTTPS migration | 301 | Permanent protocol change |