Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Put an absolute stylesheet URL in the HTML that Ruby renders, then make sure the process that displays or converts that HTML can reach the URL. For example, <link rel="stylesheet" href="https://cdn.example.com/app.css"> works in a browser and is the safest common approach for out-of-process PDF renderers. The exact setup depends on whether you are rendering a Rails page, generating a PDF with Wicked PDF or PDFKit, or using Grover with Chromium.

Use an absolute URL in the rendered HTML

For plain HTML, the stylesheet is loaded through a standard <link> element:

<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="https://cdn.example.com/app.css">
  </head>
  <body>
    <h1>Rendered page</h1>
  </body>
</html>

The URL must be reachable from the machine or container doing the rendering. A stylesheet that loads in your development browser may still fail in a production job if that environment cannot resolve the host, negotiate TLS, pass required authentication, or reach the network through its firewall.

An absolute URL identifies both the host and path. By contrast, /assets/app.css is rooted at the document’s host, and app.css is relative to the document’s base URL. Those forms can be fine in a browser-backed Rails page, but an external renderer may not know which host to use. When portability matters, use an absolute URL or configure the renderer’s base URL.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall

In Rails, generate the stylesheet link

Rails’ stylesheet_link_tag returns a link tag for each source provided. It can reference an asset name, a path relative to the document root, or a URL. For a stylesheet served from a remote host, put the fully qualified URL in the rendered layout:

<head>
  <%= stylesheet_link_tag "https://cdn.example.com/app.css" %>
</head>

If the file is managed by the Rails asset pipeline, place it in an applicable asset location such as app/assets, lib/assets, or vendor/assets, and reference it by its asset name:

<head>
  <%= stylesheet_link_tag "application" %>
</head>

The correct choice depends on where the CSS is hosted and how the deployment publishes it. A remote URL is convenient when another host serves the file. An application asset is usually a more direct fit when Rails owns the CSS. For a PDF job, ensure the relevant asset is available in the deployed environment; production PDF views may need the CSS precompiled or deliberately inlined.

Make the stylesheet available to your PDF renderer

PDF generators do not all resolve assets in the same way. In particular, an HTML-to-PDF tool that runs outside Rails cannot be assumed to inherit Rails’ asset paths or browser session. The renderer needs either a usable absolute URL, an explicit local path, or CSS supplied inline.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Renderer How to supply CSS Relative URL handling
Wicked PDF / wkhtmltopdf Use wicked_pdf_stylesheet_link_tag in the PDF layout or a fully qualified URL. Use absolute references in the layout; wkhtmltopdf runs outside the Rails application.
PDFKit Add a stylesheet path with kit.stylesheets << '/path/to/css/file', or include a link in an HTML string. Supply root_url and protocol when relative paths need a base.
Grover / Chromium Pass a style tag option with a URL, path, or CSS content. Set display_url or resolve paths before rendering; otherwise the direct Grover call defaults to http://example.com.

The APIs and behavior in this table are those documented by the respective projects; no controlled cross-renderer speed or fidelity benchmark is established here. Choose based on how your app supplies assets and the browser engine your output needs.

Wicked PDF and wkhtmltopdf

Wicked PDF delegates PDF creation to wkhtmltopdf, which runs outside the Rails process. Its documentation says that layouts using CSS, JavaScript, or images need absolute references. In a PDF-specific layout, use the Wicked PDF stylesheet helper:

<%= wicked_pdf_stylesheet_link_tag "pdf" %>

You can instead emit a fully qualified stylesheet URL. If the CSS is an asset-pipeline file, confirm that it is precompiled and served in the environment where the PDF job runs. For small stylesheets or assets, a base64 helper is another option; that avoids a separate fetch but makes the HTML larger and is less suitable for substantial CSS.

PDFKit

PDFKit accepts HTML and wkhtmltopdf options. If you instantiate a kit from an HTML string, its README shows adding a local stylesheet path like this:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
kit.stylesheets << '/path/to/css/file'

When HTML contains relative assets such as /images/logo.png, provide root_url and protocol so the renderer can resolve them against a host. PDFKit documents a limitation: stylesheets cannot be added this way when the source is supplied as a URL or a File. In that case, put the link in the source document, or supply an HTML string to which the stylesheet can be added.

Grover with Chromium

Grover offers style_tag_options entries for a URL, a path, or inline CSS content. For example, the documented option shapes are:

{ url: 'http://example.com/style.css' }
{ path: 'style.css' }
{ content: 'body { color: #222; }' }

When calling Grover directly, set display_url or preprocess relative paths so Chromium has the correct base URL. Without a configured base, Grover defaults to http://example.com; that default will not resolve paths for your own application. A stylesheet option and a document base URL solve different problems: the first tells Grover which CSS to include, while the second helps resolve relative links already present in the HTML.

Check the final HTML and the renderer’s network access

  1. Inspect what Ruby actually emitted. Find the final <link rel="stylesheet"> in the HTML sent to the renderer. Confirm that its href is the intended absolute https:// URL, not a missing asset name, unresolved template expression, or relative path.
  2. Fetch the URL from the renderer’s environment. Check DNS, TLS, firewall rules, and authentication from the same host or container that runs the PDF job. A successful request from your laptop does not establish access from a worker or production container.
  3. Check the response, not just the request. Confirm that the response has the expected CSS content and a successful status. A redirect to a login page, a blocked request, or an HTML error page can make a stylesheet appear to be ignored.
  4. Set a base URL for relative assets. For PDFKit, configure root_url and protocol; for Grover, set display_url or make paths absolute. This matters for images and fonts as well as CSS.
  5. Verify Rails PDF assets in production. Use the Wicked PDF helper or an absolute URL, and ensure any asset-pipeline CSS used in the PDF view has been precompiled or deliberately inlined.
  6. Review renderer logs. A browser-like renderer may produce a PDF even when a resource fetch fails. Use its logs alongside the HTML and stylesheet response to distinguish a missing link from a network or access problem.

Common causes of missing CSS

  • The link is relative and there is no correct base. Make the URL absolute, or set the renderer’s base URL using the renderer-specific setting.
  • The worker cannot reach the CSS host. Allow the relevant DNS, outbound network, and TLS access, or serve the asset from a host the renderer can access.
  • The CSS URL returns something other than the stylesheet. Check for authentication requirements, redirects, or an error response. Provide access deliberately rather than assuming the renderer shares a browser login.
  • A Rails asset is unavailable in production. Confirm that the asset is included in the deployed build and precompiled where required, or use the documented PDF helper or inline CSS.
  • The PDFKit source is a URL or File. Its stylesheet collection cannot be added in that source mode; include the link in the source document or render from an HTML string.
  • The document contains untrusted resource references. Do not grant arbitrary network or local-file access to user-controlled HTML. Wicked PDF warns that user-generated HTML, CSS, or JavaScript should be sanitized or restricted from requesting internal IP addresses and hostnames.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Keep remote resource loading deliberate

Loading a URL gives the renderer permission to request content from another host. That is convenient for public stylesheets, but it adds network dependencies: the host must be reachable at render time, and redirects or access controls can change what the renderer receives. Local paths avoid a network fetch but must exist in the renderer’s filesystem; inline CSS avoids a separate request but is less convenient for larger stylesheets. Choose the narrowest access method that fits the document.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

This is also a security boundary when HTML is supplied by users. A renderer with broad local-file or network access can be induced to request destinations that were never intended as part of a normal page. Sanitize or restrict user-controlled markup and resource destinations rather than opening access broadly to make one stylesheet load.

Or skip the browser setup

If your actual goal is to capture a screenshot of a public web page, rather than generate a PDF or configure a Ruby renderer, ScreenshotNeo can take the screenshot through one GET request. The target page still needs to reference the CSS you want captured; this API does not replace the stylesheet setup for your own Rails PDF output. See the ScreenshotNeo API documentation for options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo accepts cookie or consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each of those steps can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers indicate the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000, and every feature is on every plan.

Sign up for 1,000 free screenshots a month, with no card required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Does a remote stylesheet have to be hosted on the same domain as the HTML?

No. The rendering environment must be able to request the stylesheet host, and the server must permit the request. Same-origin browser rules are not a general requirement for a renderer fetching a linked CSS file.

Can I use a local CSS file instead of a URL?

Yes, where the renderer’s API supports a local path and the file exists in the environment doing the render. PDFKit and Grover document path-based options; for Rails PDF output, make sure the deployed job can access the asset.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.