Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Use an absolute stylesheet URL in the HTML you give to the renderer:
<link rel="stylesheet" href="https://cdn.example.com/app.css">
The renderer must be able to resolve and fetch that URL from its own runtime environment. In Rails, generate the link with stylesheet_link_tag; in out-of-process PDF tools, configure a real base URL, precompile assets where necessary, and verify network access from the rendering host.
The reliable pattern: an absolute URL
An absolute https:// URL is the safest common denominator when Ruby renders HTML through a separate process such as wkhtmltopdf or Chromium. Relative references such as /assets/report.css only work when the renderer has a correctly configured document origin.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://cdn.example.com/app.css">
</head>
<body>
<h1>Invoice</h1>
</body>
</html>
Use a URL that is reachable from the machine, container, or worker actually performing the render—not merely from your laptop or Rails web process. Confirm DNS, TLS, firewall rules, authentication, redirects, and the response content type.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minute#1 Best Overall
Rails HTML rendering with stylesheet_link_tag
Rails’ stylesheet_link_tag returns <link> tags for the supplied sources. It accepts an asset name, a path relative to the document root, or a URL.
Reference a remote stylesheet
<%= stylesheet_link_tag "https://cdn.example.com/app.css" %>
For a PDF-specific layout, keep the stylesheet reference in that layout so the same HTML path is used consistently by the PDF renderer.
Use the Rails asset pipeline
Stylesheets can live under app/assets, lib/assets, or vendor/assets. A typical view uses:
<%= stylesheet_link_tag "application", media: "all" %>
That is convenient for browser responses, but an external renderer still needs the generated asset URL to resolve and the asset to exist in the deployment. Production PDF jobs commonly require precompiling the CSS used by PDF views. If the asset is private, requires a session, or is served only on an internal hostname, the renderer may receive a login page or an error instead of CSS.
Wicked PDF and wkhtmltopdf
Wicked PDF runs wkhtmltopdf outside the Rails application. Its documentation requires absolute references for CSS, JavaScript, and images when those resources are used.
Rank #2
Use the Wicked PDF helper
<!-- app/views/layouts/pdf.html.erb -->
<!doctype html>
<html>
<head>
<%= wicked_pdf_stylesheet_link_tag "pdf" %>
</head>
<body>
<%= yield %>
</body>
</html>
Ensure the PDF stylesheet is precompiled in production. For a small, stable stylesheet, inlining it or using a base64 representation can avoid a second network request, but large CSS is usually easier to maintain as a compiled asset.
Emit a fully qualified URL
<link rel="stylesheet" href="https://files.example.com/assets/pdf.css">
Do not assume that a URL such as /assets/pdf.css is meaningful to a process with no browser origin. Make the host and scheme explicit.
Protect untrusted content
Wkhtmltopdf can request network resources while rendering. Sanitize user-generated HTML and CSS, or restrict requests so untrusted input cannot reach internal IP addresses and hostnames. This is both a correctness and server-security requirement.
PDFKit: supply stylesheets and a base URL
PDFKit wraps wkhtmltopdf and exposes stylesheet and URL options.
Add a stylesheet by filesystem path
kit = PDFKit.new(html)
kit.stylesheets << "/path/to/css/file.css"
pdf_bytes = kit.to_pdf
This approach bypasses remote fetching for the CSS file. The path must exist on the host running the job.
Rank #3
Resolve relative URLs with root_url and protocol
kit = PDFKit.new(
html,
root_url: "example.com",
protocol: "https"
)
pdf_bytes = kit.to_pdf
With these options, relative image, font, and stylesheet paths can resolve against a known origin. PDFKit notes that stylesheet options cannot be added when the source is supplied as a URL or a File; put the <link> inside that source document or pass an HTML string instead.
Grover and Chromium
Grover uses Chromium and supports stylesheet injection as a URL, local path, or inline content.
Recommended Free Tools
Inject a URL, path, or CSS string
grover = Grover.new(
html,
style_tag_options: [
{ url: "https://cdn.example.com/app.css" },
{ path: "app/assets/builds/pdf.css" },
{ content: "body { color: #222; }" }
]
)
pdf = grover.to_pdf
Use only the option you need; the example shows the three supported forms. When calling Grover directly, set display_url or preprocess relative paths. Chromium needs a base URL, and without one Grover defaults to http://example.com, which may not match your deployment.
grover = Grover.new(
html,
display_url: "https://app.example.com/reports/42"
)
image = grover.to_png
A deliberately chosen display URL also gives relative fonts, images, and CSS a predictable origin.
Choose the implementation by renderer
| Renderer | Engine | CSS injection | Base-URL setting | Deployment concern |
|---|---|---|---|---|
| Rails view | Application HTML response | stylesheet_link_tag, URL, asset name, or path |
Browser/request URL | Make compiled assets available in the environment serving the view |
| Wicked PDF / wkhtmltopdf | WebKit-based executable | wicked_pdf_stylesheet_link_tag or absolute <link> |
Prefer fully qualified URLs | Precompile PDF CSS; restrict untrusted network requests |
| PDFKit | wkhtmltopdf | kit.stylesheets or a link in HTML |
root_url and protocol |
Stylesheet options do not apply to URL/File sources |
| Grover | Chromium | style_tag_options: URL, path, or content |
display_url |
Without a base, relative URLs use Grover’s default origin |
There is no universal speed or fidelity ranking among these renderers. Output depends on the engine, CSS, fonts, JavaScript, network conditions, and options used.
Rank #4
A repeatable debugging checklist
- Inspect the final HTML. Confirm that the emitted
<link>contains the expected absolutehttps://URL, or that a local path is present on the renderer host. - Fetch from the renderer environment. Run an HTTP request from the same container, VM, or worker. Check DNS resolution, TLS certificates, proxy settings, firewall policy, and authentication.
- Check the response. Verify a successful status and CSS content type. A redirect to a sign-in page, an HTML error document, or a blocked request often appears as “CSS ignored.”
- Set the correct base. Use PDFKit’s
root_url/protocolor Grover’sdisplay_urlwhenever the document contains relative URLs. - Verify asset deployment. Confirm that the PDF bundle was precompiled and that the generated filename matches the link emitted by Rails.
- Check renderer logs. Look for certificate failures, DNS errors, blocked requests, missing files, and timeouts. Capture a minimal HTML document with one stylesheet to isolate the failing layer.
- Remove conflicting CSS assumptions. A stylesheet can load successfully yet appear ineffective because a later rule, selector specificity, media query, or unsupported browser feature overrides it.
- Restrict untrusted input. Sanitize HTML/CSS and deny access to internal destinations when users can influence rendered content.
Common failures and precise fixes
“The link works in Chrome but not in the PDF”
The PDF process may not share your browser’s cookies, DNS, proxy, or network route. Publish the CSS at a renderer-reachable HTTPS URL, or provide a local stylesheet path through PDFKit/Grover.
Free tools Windows power users keep installed
One-click scans. No signup required.
“Relative images and fonts are missing too”
That indicates a missing or incorrect document origin. Set root_url and protocol in PDFKit, or display_url in Grover; alternatively rewrite resources to absolute URLs.
“Rails generated a fingerprinted file that returns 404”
Precompile the relevant assets in the production build and deploy the generated files wherever the renderer fetches them. Check the exact URL in the rendered HTML rather than guessing the filename.
“The stylesheet URL returns 200, but styles do not apply”
Inspect the response body and content type. A 200 response containing an authentication page or proxy notice is not CSS. Also check media attributes and CSS ordering.
If your goal is a clean screenshot or PDF of the rendered page rather than managing a Ruby renderer, ScreenshotNeo provides a single HTTP endpoint. It accepts the consent banner like a visitor, removes more than 60 known consent platforms plus newsletter popups and chat widgets before capture, and bills only clean shots. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; the response identifies the page verdict and billing status in headers. See the ScreenshotNeo documentation for all options. You can also use the supplied Ruby application as the caller: Do these 3 things before closing this tab: ScreenshotNeo also offers an MCP server with Yes, but the renderer must receive the required credentials through its supported headers, cookies, or a controlled local asset. A browser session on your workstation does not automatically carry over to wkhtmltopdf or Chromium. Inlining can remove a network dependency for small stylesheets, but it increases document size and complicates maintenance. Use it selectively; otherwise serve a precompiled stylesheet or inject a local file. Different engines support different CSS and JavaScript features. Identify whether the job uses wkhtmltopdf/WebKit or Chromium, then adapt the stylesheet or choose a renderer whose engine supports the required feature. What’s actually slowing this PC down? Pick the symptom - the matching free tool is one click away. Yes, but the renderer must receive the required credentials through its supported headers, cookies, or a controlled local asset. A browser session on your workstation does not automatically carry over to wkhtmltopdf or Chromium. Inlining can remove a network dependency for small stylesheets, but it increases document size and complicates maintenance. Use it selectively; otherwise serve a precompiled stylesheet or inject a local file. Different engines support different CSS and JavaScript features. Identify whether the job uses wkhtmltopdf/WebKit or Chromium, then adapt the stylesheet or choose a renderer whose engine supports the required feature. 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.Or skip the browser setup
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webpBest Value
require "net/http"
require "uri"
uri = URI("https://api.screenshotneo.com/v1/shot")
uri.query = URI.encode_www_form(access_key: ENV.fetch("SCREENSHOTNEO_KEY"), url: "https://stripe.com")
File.binwrite("shot.webp", Net::HTTP.get(uri))Other client examples
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}`);take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. Every plan includes the full feature set, including selector capture, full-page lazy-image loading, custom CSS and JavaScript, waits, request blocking, cookies, headers, device and viewport controls, PDF settings, caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, and a usage API. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up free.FAQ
Can a stylesheet URL require authentication?
Should I inline all CSS for PDF generation?
Why does a modern CSS rule work in a browser but not in my PDF?
Frequently Asked Questions
Can a stylesheet URL require authentication?
Should I inline all CSS for PDF generation?
Why does a modern CSS rule work in a browser but not in my PDF?
Quick Recap

