{"id":13879524,"url":"https://github.com/testdouble/static-rails","last_synced_at":"2025-07-16T15:32:28.248Z","repository":{"id":37881790,"uuid":"260359351","full_name":"testdouble/static-rails","owner":"testdouble","description":"Build \u0026 serve static sites (e.g. Jekyll, Hugo) from your Rails app","archived":false,"fork":false,"pushed_at":"2023-01-21T02:49:12.000Z","size":2044,"stargazers_count":157,"open_issues_count":48,"forks_count":7,"subscribers_count":11,"default_branch":"main","last_synced_at":"2024-11-18T02:43:33.604Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/testdouble.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-05-01T01:59:37.000Z","updated_at":"2024-10-12T09:38:33.000Z","dependencies_parsed_at":"2023-02-12T07:31:24.690Z","dependency_job_id":null,"html_url":"https://github.com/testdouble/static-rails","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testdouble%2Fstatic-rails","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testdouble%2Fstatic-rails/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testdouble%2Fstatic-rails/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testdouble%2Fstatic-rails/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/testdouble","download_url":"https://codeload.github.com/testdouble/static-rails/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226143895,"owners_count":17580245,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-06T08:02:23.805Z","updated_at":"2024-11-24T08:31:24.510Z","avatar_url":"https://github.com/testdouble.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# static-rails\n\n[![CircleCI](https://circleci.com/gh/testdouble/static-rails.svg?style=svg)](https://circleci.com/gh/testdouble/static-rails)\n\n## Build and serve your static sites from your Rails app\n\n**tl;dr in development, static-rails runs your static site generators \u0026\nproxies requests; in production, it compiles and serves the final assets**\n\nStatic site generators are hot right now. Maybe you're hip with \"the\n[Jamstack](https://jamstack.org)\", or maybe your API documentation is generated\nby [Hugo](https://gohugo.io), or maybe your marketing folks use\n[Jekyll](https://jekyllrb.com) for the company blog.\n\nUp until now, compiling static assets with any degree of sophistication beyond\ndumping them in your app's `public/` directory represented a significant\ndeviation from the \"Rails Way\". But the alternative—keeping your static sites\nwholly separate from your Rails app—raises myriad operational challenges, from\ntracking multiple git repositories, to managing multiple server configurations,\nto figuring out a way to share common JavaScript and CSS assets between them\nall.\n\nNo longer! static-rails lets you use your static asset generators of choice\nwithout forcing you to abandon your monolithic Rails architecture.\n\nHere's what it does:\n\n* In development, static-rails launches your sites' local servers and then\n  proxies any requests to wherever you've mounted them in your Rails app so you\n  can start a single server and transition work between your static sites and\n  Rails app seamlessly\n\n* When deploying, static-rails will compile all your static assets when `rake\n  assets:precompile` is run, meaning your assets will be built automatically\n  when pushed to a platform like Heroku\n\n* In production, static-rails will serve your sites' compiled assets from disk\n  with a similar features and performance to what you're familiar with if you've\n  ever hosted files out of your `public/` directory\n\n## Install\n\nAdd this to your Gemfile:\n\n```\ngem \"static-rails\"\n```\n\nThen run this generator to create a configuration file\n`config/initializers/static.rb`:\n\n```\n$ rails g static_rails:initializer\n```\n\nYou can check out the configuration options in the [generated file's\ncomments]().\n\nWant an example of setting things up? You're in luck, there's an [example\napp](/example) right in this repo!\n\n## Configuring the gem\n\n**(Want to dive right in? The generated initializer [enumerates every\noption](/lib/generators/templates/static.rb) and the [example app's\nconfig](https://github.com/testdouble/static-rails/blob/main/example/config/initializers/static.rb)\nsets up 4 sites.)**\n\n### Top-level configuration\n\nSo, what should you stick in your initializer's `StaticRails.config do |config|`\nblock? These options are set right off the `config` object and control the\noverall behavior of the gem itself, across all your static sites:\n\n* **config.proxy_requests** (Default: `!Rails.env.production?`) when true,\n  the gem's middleware requests that match where you've mounted your static site\n  and proxy them to the development server\n\n* **config.serve_compiled_assets** (Default: `Rails.env.production?`) when true,\n  the gem's middleware will find your static assets on disk and serve them using\n  the same code that Rails uses to serve files out of `public/`\n\n* **config.ping_server_timeout** (Default: `5`) the number of seconds that (when\n  `proxy_requests` is true, that the gem will wait for a response from a static\n  site's server on any given request before timing out and raising an error\n\n* **config.set_csrf_token_cookie** (Default: `false`) when true, the gem's\n  middleware will set a cookie named `_csrf_token` with each request of your\n  static site. You can use this to set the `'x-csrf-token'` header on any\n  requests from your site back to routes hosted by the Rails app that are\n  [protected from CSRF\n  forgery](https://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf)\n  (if you're not using Rails' cookie store for sessions or you're okay with API\n  calls bypassing Rails CSRF, leave this off)\n\n### Configuring your static sites themselves\n\nTo tell the gem about your static sites, assign an array of hashes as `sites`\n(e.g. `config.sites = [{…}]`). Each of those hashes have the following options:\n\n* **name** (Required) A unique name for the site (primarily used for\n  logging)\n\n* **source_dir** (Required) The file path (relative to the Rails app's root) to\n  the static site's project directory\n\n* **url_subdomain** (Default: `nil`) Constrains the static site's assets to only\n  be served for requests to a given subdomain (e.g. for a Rails app hosting\n  `example.com`, a Hugo site at `blog.example.com` would set this to `\"blog\"`)\n\n* **url_root_path** (Default: `/`) The base URL path at which to mount the\n  static site (e.g. if you want your Jekyll site hosted at `example.com/docs`,\n  you'd set this to `/docs`). For most static site generators, you'll want to\n  configure it to serve assets from the same path so links and other references\n  are correct (see below for examples)\n\n* **url_skip_paths_starting_with** (Default: `[]`) If you want to mount your\n  static site to `/` but allow the Rails app to serve APIs from `/api`, you can\n  set the path prefix `[\"/api\"]` here to tell the gem's middleware not to try to\n  proxy or serve the request from your static site, but rather let Rails handle\n  it\n\n* **start_server** (Default `!Rails.env.production?) When true, the gem will\n  start the site's server (and if it ever exits, restart it) as your Rails app\n  is booting up. All output from the server will be forwarded to STDOUT/STDERR\n\n* **server_command** (Required if `start_server` is true) the command to run to\n  start the site's server, from the working directory of `source_dir` (e.g.\n  `hugo server`)\n\n* **ping_server** (Default: true) if this and `start_server` are both true, then\n  wait to proxy any requests until the server is accepting TCP connections\n\n* **env** (Default: `{}`) any environment variables you need to pass to either\n  the server or compile commands (e.g. `env: {\"BUNDLE_PATH\" =\u003e\n  \"vendor/bundle\"}`). Note that this configuration file is Ruby, so if you need\n  to provide different env vars based on Rails environment, you have the power\n  to do that!\n\n* **server_host** (Default: `localhost`) the host your static site's server will\n  run on\n\n* **server_port** (Required if `proxy_requests` is true) the port your static\n  site's server will accept requests on\n\n* **server_path** (Default: `\"/\"`) the root URL path to which requests should be\n  proxied\n\n* **compile_comand** (Required) the command to be run by both the\n  `static:compile` and `assets:precompile` Rake commands (e.g. `npm run build`),\n  with working directory set to the site's `source_dir`\n\n* **compile_dir** (Required when `serve_compiled_assets` is true) the root file\n  path to which production assets are compiled, relative to the site's\n  `source_dir`\n\n* **compile_404_file_path** (Optional) When `serve_compiled_assets` is true,\n  this file (relative to the `compile_dir`) will be served whenever the\n  request's path does not match a file on disk\n\n## Configuring your static site generators\n\nAssuming you won't be mounting your static site to your app's root `/` path,\nyou'll probably need to configure its base URL path somehow. Here are\nsome tips (and if your tool of choice isn't listed, we'd love a [pull\nrequest](https://github.com/testdouble/static-rails/edit/main/README.md)!):\n\n### Using Jekyll\n\nIf you have a Jekyll app that you plan on serving from a non-root path (say,\n`/docs`), then you'll need to set the `baseurl` in `_config.yml`:\n\n```yml\nbaseurl: \"/docs\"\n```\n\n(Note that this means running the Jekyll application directly using `bundle exec\njekyll serve` will also start serving at `http://127.0.0.1:4000/docs/`)\n\n### Using Hugo\n\nIf you are mounting your [Hugo](https://gohugo.io) app to anything but the root\npath, you'll need to specify that path in the `baseURL` of your root\n`config.toml` file, like so:\n\n```toml\nbaseURL = \"http://blog.example.com/docs\"\n```\n\nAdditionally, getting Hugo to play nicely when being proxied by your Rails\nserver in development and test can be a little tricky, because most themes will\nrender fully-qualified URLs into markup when running the `hugo server` command.\nThat means if you're forwarding `http://blog.localhost:3000/docs` to a Hugo\nserver running on `http://localhost:1313`, it's very likely the static files\n(e.g. all the links on the page) will have references to\n`http://localhost:1313`, which may result in accidentally navigating away from\nyour Rails development server unexpectedly.\n\nTo mitigate this, there are a few things you can do:\n\n* Favor `.RelPermalink` in your templates over `.Permalink` where possible.\n* In place of referring to `{{.Site.BaseURL}}` in your templates, generate a\n  base path with `{{ \"/\" | relURL }}` (given the above `baseURL`, this will\n  render `\"/marketing/\"`)\n\nAlso, because Hugo will serve `/livereload.js` from the root, live-reloading probably\nwon't work in development when running through the static-rails proxy.\nYou might consider disabling it with `--disableLiveReload` unless you're serving\nHugo from a root path (\"`/`\").\n\nA static-rails config for a Hugo configuration in `sites` might look like:\n\n```rb\n  {\n    name: \"docs\",\n    url_root_path: \"/docs\",\n    source_dir: \"static/docs\",\n    server_command: \"hugo server\",\n    server_port: 8080,\n    compile_command: \"hugo\",\n    compile_dir: \"static/docs/public\"\n  }\n```\n\n### Using Eleventy\n\nIf you are mounting your [Eleventy](https://www.11ty.dev) app to anything but\nthe root path, you'll want to configure a path prefix in `.eleventy.js`\n\n```js\nmodule.exports = {\n  pathPrefix: \"/docs/\"\n}\n```\n\nAlternatively, you can specify this from the command line with `--pathprefix\n/docs`.\n\nA static-rails config for an Eleventy configuration in `sites` might look like:\n\n```rb\n  {\n    name: \"docs\",\n    url_root_path: \"/docs\",\n    source_dir: \"static/docs\",\n    server_command: \"npx @11ty/eleventy --serve --pathprefix /docs\",\n    server_port: 8080,\n    compile_command: \"npx @11ty/eleventy --pathprefix /docs\",\n    compile_dir: \"static/docs/_site\"\n  }\n```\n\n### Using Gatsby\n\n\u003cstrong\u003e ⚠️ Gatsby is unlikely to work in development mode, due to [this\nissue](https://github.com/gatsbyjs/gatsby/issues/18143), wherein all the assets\nare actually served over a socket.io WebSocket channel and not able to be\nproxied effectively. ⚠️  \u003c/strong\u003e\n\nIf you're mounting a [Gatsby](https://www.gatsbyjs.org) site to a non-root path\n(e.g. in static-rails, you've configured its `url_root_path` to, say,\n`careers`), then you'll want to configure the same root path in Gatsby as well,\nso that its development servers and built assets line up correctly.\n\nTo do this, first add the `pathPrefix` property to `gatsby-config.js`:\n\n```js\nmodule.exports = {\n  pathPrefix: `/careers`,\n  // …\n}\n```\n\nNext, add the flag `--prefix-paths` to both the Gatsby site's `server_command`\nand `compile_command`, or else the setting will be ignored.\n\nA static-rails config for a Gatsby configuration in `sites` might look like:\n\n```rb\n  {\n    name: \"gatsby\",\n    url_root_path: \"/docs\",\n    source_dir: \"static/docs\",\n    server_command: \"npx gatsby develop --prefix-paths\",\n    server_port: 8000,\n    compile_command: \"npx gatsby build --prefix-paths\",\n    compile_dir: \"static/docs/public\"\n  },\n```\n\n## Code of Conduct\n\nThis project follows Test Double's [code of\nconduct](https://testdouble.com/code-of-conduct) for all community interactions,\nincluding (but not limited to) one-on-one communications, public posts/comments,\ncode reviews, pull requests, and GitHub issues. If violations occur, Test Double\nwill take any action they deem appropriate for the infraction, up to and\nincluding blocking a user from the organization's repositories.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftestdouble%2Fstatic-rails","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftestdouble%2Fstatic-rails","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftestdouble%2Fstatic-rails/lists"}