SEO & Sitemaps
RailsFast has been designed for SEO and discoverability from the ground up. Your project, immediately upon deployment, should score 100/100 on SEO and close to 100/100 on overall performance on Google PageSpeed Insights:
Other than technical improvements and good technical architecture, your main two ways of affecting on-page SEO on your RailsFast app are: metadata and the sitemap.
One hostname, one canonical URL per page
Google treats www.yourapp.com and yourapp.com as two sites, and ?page=1 or ?utm_source=… as different pages, unless you tell it otherwise. Left alone, it indexes both and files half of them as "duplicate without user-selected canonical" in Search Console: on one RailsFast app that was 687 URLs, three quarters of them on the www. host, because Kamal's proxy answers for both hostnames out of the box.
RailsFast handles this with the CanonicalHost concern, included in ApplicationController:
- Every
GETonwww.<domain>is permanently redirected (301) to the same path and query ondomainfromconfig/railsfast/railsfast.yml. Other hosts (an IP for health checks,localhost, the Android emulator's10.0.2.2) are served normally. - Every HTML page declares a self-referential
<link rel="canonical">on that host with the query string dropped, through themeta-tagsgem.
When a parameter makes a genuinely different page that deserves its own place in the index, keep it in the canonical from the action:
def index
@pagy, @posts = pagy(Post.all)
set_meta_tags canonical: canonical_url_with(:page) if @pagy.page > 1
end
canonical_url_with(:country, :page) keeps only the named parameters; set_meta_tags merges, so the action's call wins over the default.
If you would rather your canonical host be www., change the concern's redirect_www_to_canonical_host and canonical_host accordingly, and set domain to the www form.
Metadata
We use the meta-tags gem to handle on-page metadata. Everything is already configured and you'll see examples throughout the codebase.
Setting title and description in views
The easiest way to set page metadata is directly in your view files using the title and description helpers:
<% title "My page" %>
<% description "This is the SEO description for my page" %>
You can also combine them in a single ERB block:
<%
title "My page"
description "This is the SEO description for my page"
%>
Never use content_for :title (or content_for :meta_description) to set a page title. It looks like it works, and it silently does nothing.
Your layouts render the <head> with display_meta_tags(...). The meta-tags gem has its own store, written by title / description / set_meta_tags, and it never reads content_for. No public RailsFast layout reads content_for(:title), so a page using content_for :title quietly keeps the app-wide default title, in the browser tab and in every og:title. Nothing raises, nothing warns, and the <h1> on the page still looks right, so it is easy to miss for a long time.
You will see content_for :title in app/views/madmin/**. That is correct there and only there: the admin has its own layout, layouts/madmin/dashboard.html.erb, which puts content_for(:title) in front of <app name> Admin in the <title>. Do not copy the pattern into public pages.
Dynamic titles and descriptions
For pages with dynamic content, you can use Ruby expressions:
<%
title @product ? "#{@product.name} - Shop" : "All Products"
description @product ?
"Buy #{@product.name} for only $#{@product.price}. #{@product.short_description}" :
"Browse our full catalog of products with fast shipping."
%>
Setting metadata in controllers
Alternatively, set metadata in your controller actions with set_meta_tags:
def show
@product = Product.find(params[:id])
set_meta_tags title: @product.name,
description: @product.description.truncate(160)
end
Open Graph images
Set a custom OG image for social sharing:
set_meta_tags og: { image: "https://example.com/og-image.jpg" }
Private pages (noindex)
For pages that shouldn't appear in search results (like user dashboards, account settings, or authenticated areas), use noindex and nofollow:
<%
title "Your Account Settings"
description "Manage your account preferences and settings."
set_meta_tags noindex: true, nofollow: true
%>
This tells search engines not to index the page or follow its links, keeping private content out of search results.
Sitemaps
Your app sitemap is available at /sitemaps/sitemap.xml.gz
If you're using the CMS/blog system, the sitemap automatically includes all your content with smart behavior (auto-discovery, visibility flags, priority assignment, empty section handling). The footer also adapts automatically to your content structure. See the Sitemap and Footer sections in the CMS docs for details.
Custom URLs
You can configure what URLs go in the sitemap by editing your config/sitemap.rb file. You can dynamically add thousands of URLs by iterating over all your database records. We leverage the sitemap_generator gem for this, read their docs to know what's possible!
You can also enable automatic sitemap discovery in your robots.txt file by uncommenting the sitemap line at the end of the file.
Automatic refresh
The sitemap is automatically regenerated daily via SitemapRefreshJob (configured in config/recurring.yml). To regenerate manually, run rails sitemap:refresh:no_ping or trigger the job from console with SitemapRefreshJob.perform_later.
Cloudflare
If the domain is proxied through Cloudflare (the recommended default), four zone settings matter for SEO and page speed. None of them are on by default, and one default actively hurts.
Cache HTML at the edge. Cloudflare never caches HTML unless a Cache Rule says so, whatever Cache-Control your app sends. Without one, every public page is cf-cache-status: DYNAMIC and hits your server, and any s-maxage you set is ignored. Add a Cache Rule (Caching → Cache Rules) for your canonical host with Eligible for cache, edge TTL and browser TTL both set to Respect origin, and an expression that bypasses the cache when a session or remember-me cookie is present, so signed-in visitors never receive a cached anonymous page:
(http.host eq "yourapp.com" and not http.cookie contains "_yourapp_session" and not http.cookie contains "remember_user_token")
Rails pages are private by default, so only responses your app explicitly marks public with an s-maxage get cached. Check with curl -sI https://yourapp.com/some-page | grep cf-cache-status: the second request should say HIT. Purge the cache after a deploy that changes cached pages.
Redirect www at the edge. CanonicalHost already 301s www. in Rails, but a Redirect Rule (Rules → Redirect Rules → the "Redirect from WWW to Root" template) answers from the edge without touching your server.
Turn Email Address Obfuscation off (Scrape Shield). It rewrites every mailto: link into a /cdn-cgi/l/email-protection URL that Google then crawls and reports as a 404, and it injects a script into every page.
Turn Always Use HTTPS on (SSL/TLS → Edge Certificates). Rails's force_ssl already redirects, but doing it at the edge is one less round trip.
Sitemaps and robots.txt are served fine through the proxy; if Search Console reports it cannot fetch the sitemap, check for a WAF or bot rule challenging Googlebot before anything else.
Slugs
If you want to create SEO-friendly URLs, I recommend using the slugifiable gem.
This will allow you to have semantic URLs like
https://myapp.com/products/big-red-backpack
instead of
https://myapp.com/products/14
The slugifiable gem does not come pre-configured by default in RailsFast because it's very app-dependent, but it should take you just ~5 minutes to set up.
Turning it on: uncomment gem "slugifiable" in the Gemfile (it's in the opt-in block near the bottom), run bundle install, and follow the slugifiable README to add slugs to your models.