What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Use an absolute, reachable stylesheet URL—or make Rails emit one. A browser can resolve /assets/print.css inside your application, but PDFKit and Wicked PDF hand HTML to a separate renderer. That process needs a URL it can reach, a correctly configured base URL, or a local file it is explicitly allowed to read. For Rails, use wicked_pdf_stylesheet_link_tag with a precompiled asset. For PDFKit, use a fully qualified URL or set root_url and protocol when rendering raw HTML. If the CSS host is private, give the renderer network access and authentication, or download and inline the stylesheet before conversion.
Why a stylesheet works in the browser but disappears from the PDF
PDF generation is not performed by the browser tab that loaded your Rails page. Wicked PDF starts wkhtmltopdf outside the Rails process; its maintainers state that “the wkhtmltopdf binary is run outside of your Rails application; therefore, your normal layouts will not work.” The renderer receives HTML and must independently resolve every stylesheet, font, image and script reference.
A relative link such as <link rel="stylesheet" href="/assets/invoice.css"> has no useful origin when the input is an HTML string or a temporary file. A protocol-relative link such as //cdn.example.com/invoice.css also needs a protocol. In production, an asset host, digest filename or authentication wall can create a second failure even when development appeared correct.
The dependable rule is to choose one of these forms:
#1 Best Overall
- A public, fully qualified URL, for example
https://cdn.example.com/assets/invoice-8f31.css. - A relative URL plus a configured base, such as PDFKit’s
root_urlandprotocoloptions. - A local stylesheet path that the renderer is allowed to read.
- CSS downloaded or inlined before conversion, when the source is private or unreachable from the renderer.
Absolute does not mean merely “starts with a slash.” It means the renderer can resolve and fetch the complete address at conversion time.
Pick the loading strategy that matches your Ruby PDF library
| Path | HTML input | CSS approach | Important deployment concern |
|---|---|---|---|
| PDFKit | Raw HTML, a local file, or a URL | Fully qualified URL; for raw HTML, a local path or a relative URL resolved with root_url and protocol |
The stylesheet collection does not add stylesheets when the source itself is a URL or file |
| Wicked PDF | Rails view rendered by wkhtmltopdf |
wicked_pdf_stylesheet_link_tag, an absolute asset URL, or a CDN URL |
Precompile the PDF stylesheet and ensure production asset-host settings produce an absolute address |
| wkhtmltopdf directly | URL or HTML file | --user-style-sheet or a link in the HTML; local-file permissions must match your setup |
Qt WebKit rendering and local-file/network security settings affect what loads |
| Prawn | Ruby drawing instructions, not an HTML page | No HTML stylesheet loading | Use Prawn when you want to draw the PDF directly rather than render HTML/CSS |
PDFKit’s README documents the distinction between raw HTML and URL/file sources. Wicked PDF’s README documents the absolute-reference requirement for CSS, JavaScript and images. The underlying renderer is wkhtmltopdf, an open-source command-line tool that uses Qt WebKit.
PDFKit: load a remote stylesheet or resolve a Rails-relative URL
Option 1: put a complete URL in the HTML
This is the least ambiguous arrangement for raw HTML. The renderer must be able to make an HTTPS request to the host.
html = <<~HTML
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://cdn.example.com/assets/pdf.css">
</head>
<body>
<h1>Invoice 1042</h1>
<p>Thank you for your order.</p>
</body>
</html>
HTML
kit = PDFKit.new(html)
kit.to_file("tmp/invoice.pdf")
Use a versioned or digest URL so a deployment does not serve an old stylesheet from a cache. Check the URL from the same machine, container or job that runs PDFKit—not only from your laptop.
Recommended Free Tools
Option 2: keep a relative link and configure the base URL
When application code generates the HTML, PDFKit can resolve relative references with root_url and protocol:
Rank #2
html = ApplicationController.render(
template: "invoices/show",
assigns: { invoice: invoice }
)
kit = PDFKit.new(
html,
root_url: "https://app.example.com",
protocol: "https"
)
kit.to_file("tmp/invoice-#{invoice.id}.pdf")
The view can then contain:
<link rel="stylesheet" href="/assets/pdf.css">
The base must include the host that serves the asset. If production uses a separate asset host, use that host in the generated absolute URL or configure Rails’ asset host accordingly.
Do not rely on the stylesheet collection for URL or file sources
PDFKit supports adding local stylesheet paths when the source is raw HTML. That mechanism does not add stylesheets when the source is supplied as a URL or a file. In those modes, put the link in the HTML itself, or switch to a raw-HTML workflow in which a local path is appropriate.
Wicked PDF in Rails: use the helper and precompile the asset
View markup
Wicked PDF provides a helper that emits a renderer-friendly reference:
<!-- app/views/invoices/show.pdf.erb -->
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<%= wicked_pdf_stylesheet_link_tag "pdf" %>
</head>
<body>
<h1>Invoice <%= @invoice.number %></h1>
<p><%= @invoice.customer_name %></p>
</body>
</html>
Alternatively, link directly to a publicly reachable asset:
<link rel="stylesheet" href="https://cdn.example.com/assets/pdf.css">
Controller and asset setup
# app/controllers/invoices_controller.rb
def show
@invoice = Invoice.find(params[:id])
respond_to do |format|
format.html
format.pdf do
render pdf: "invoice-#{@invoice.number}", template: "invoices/show.pdf.erb"
end
end
end
Keep the PDF stylesheet in the asset pipeline and precompile it for production. A digest filename that was never built, or an asset host that resolves only inside the Rails process, leaves wkhtmltopdf with a dead link. The helper is Rails-specific; outside Rails, emit an absolute URL yourself.
Rank #3
Wicked PDF’s maintainers put the requirement plainly: “If you plan to use any CSS, JavaScript, or image files, you must modify your layout so that you provide an absolute reference to these files.”
Using wkhtmltopdf directly
If you call the renderer without a Ruby wrapper, put the stylesheet in the HTML or use its user-stylesheet setting:
wkhtmltopdf --user-style-sheet /srv/pdf/pdf.css input.html output.pdf
Run wkhtmltopdf --help and consult the usage documentation for the options available in your installed build. The lower-level page settings include a userStyleSheet URL/path and load.blockLocalFileAccess; the latter matters when a remote stylesheet references local images or fonts. The corresponding settings are described in the libwkhtmltox page-settings documentation.
Do not enable broad local-file access merely to make one asset load. A PDF job that accepts untrusted HTML could otherwise be given access to files or network locations it should never read. Prefer a controlled asset directory, a public read-only asset host, or inlined CSS.
Private CSS, authentication and network reachability
A URL that works in your browser may still fail in a background PDF job because the job has different DNS, firewall rules, proxy settings or credentials. For a private stylesheet:
Rank #4
- Confirm that the PDF process can resolve the hostname and establish HTTPS connectivity.
- Confirm that the certificate chain is trusted by the renderer’s operating environment.
- Provide any required authentication in a way supported by your deployment, or fetch the CSS in Ruby before conversion.
- Rewrite relative
url(...)references inside the CSS if downloading it; fonts and background images need reachable URLs too. - Use a temporary, controlled file or inline
<style>block, then remove it after conversion.
The documentation establishes the need for absolute paths and renderer configuration, but it does not guarantee that every remote authentication scheme will work. If the endpoint requires an interactive login, browser-only token or JavaScript challenge, make a server-side copy or expose a narrowly scoped asset URL instead.
CSS and asset details that commonly affect the final PDF
- Print rules: Put PDF-specific declarations in the stylesheet you load for the PDF view. Do not assume the browser’s screen rules are the intended print design.
- Fonts: A font referenced by CSS is another network or file request. Make its URL reachable from the renderer and verify licensing and format support.
- Images: Use absolute image URLs or permitted local paths. A stylesheet can load while its background images remain missing.
- Relative CSS URLs: Resolve them relative to the stylesheet’s own location, not necessarily the HTML page. Moving CSS to a CDN can therefore require changing
url(...)paths. - Timing: If CSS is injected by JavaScript, ensure the renderer waits long enough; a static link is more reliable for a PDF layout.
- Cache and deploys: Digest or version your CSS and avoid changing a file in place while jobs are running.
Troubleshooting: symptom, cause and fix
| Symptom | Likely cause | Fix |
|---|---|---|
| All styling is absent | The link is relative and the renderer has no base URL | Use an HTTPS URL, or set PDFKit’s root_url and protocol |
| Works in development, fails in production | The PDF asset was not precompiled or the production asset host is not absolute | Precompile the PDF stylesheet and inspect the rendered HTML for its final URL |
| PDFKit ignores an added stylesheet | The source was supplied as a URL or file | Place the <link> in the HTML, or use raw HTML with a supported local path |
| CSS URL returns an error in the job | DNS, firewall, certificate or authentication differs from the browser | Fetch the URL from the renderer’s host and make the asset publicly reachable or download it first |
| Styles load but images or fonts do not | Nested url(...) references point to unreachable locations |
Make every nested resource absolute or permit the required local directory |
| Local assets fail after tightening security | Local-file access is blocked | Use a controlled asset URL, or explicitly allow only the required directory; do not broadly expose the filesystem |
| Layout differs from Chrome | wkhtmltopdf uses Qt WebKit rather than a current browser engine | Test against the renderer’s engine and simplify unsupported CSS; choose a modern browser service when fidelity is critical |
For diagnosis, save the exact HTML sent to the renderer, open it from the same execution environment, and inspect the stylesheet response status. This separates URL resolution from CSS compatibility.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Performance, reliability and cost decisions
Local wkhtmltopdf avoids a per-document hosted-renderer request but makes you responsible for installing and updating a binary, fonts, network access and concurrency. Remote CSS adds DNS and HTTP latency to every uncached job. Inlining a small, stable stylesheet removes one request; downloading a large stylesheet once and reusing a controlled copy can help batch jobs, but you must refresh it when the design changes.
For sensitive documents, decide whether the renderer may contact public hosts at all. A self-contained HTML package is easier to audit. For public pages, a hosted headless-browser service can reduce binary maintenance, but verify its PDF options, authentication model, data handling and pricing before moving production documents.
Or skip the browser setup
ScreenshotNeo is a hosted website screenshot API and MCP server that can return a clean screenshot or PDF from one GET request. It accepts the page like a visitor, removes cookie-consent banners, newsletter popups and chat widgets before capture, and reports whether the result was billed. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed. Its MCP server exposes take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →For the complete parameter list and PDF settings, see the ScreenshotNeo documentation.
Best Value
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
You can also control full-page capture, lazy-image loading, viewport and device presets, dark mode, retina scale, custom CSS and JavaScript, clicks, waits, blocked requests, cookies, headers, user agents, timezone, geolocation, transparent backgrounds, resizing, caching TTL, signed links, asynchronous webhooks and bulk capture. Every response includes X-Page-Verdict and X-Billed headers so a failed or unclean page is distinguishable from a billable result.
The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing provides two months free. Create a free ScreenshotNeo account to try it without a card.
Frequently Asked Questions
Can I use a protocol-relative stylesheet URL?
Yes, but only when the renderer has a protocol to apply. Supplying protocol: "https" in PDFKit or writing the complete HTTPS URL removes that ambiguity.
Is a Rails asset helper enough for a PDF job?
Only if the helper outputs an absolute, reachable reference and the asset was built for the current environment. Inspect the generated HTML rather than assuming the helper’s browser behavior carries over.
When should I choose Prawn instead?
Choose Prawn when the document is best expressed as Ruby drawing commands and you do not need HTML/CSS rendering. It will not make an HTML <link> stylesheet load automatically.
Quick Recap
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.

