Skip to main content
Ssnap Docs
Platform

URL restrictions

Which targets Ssnap will and will not fetch, and why.

Ssnap fetches urls on your behalf, so it refuses anything that is not publicly routable. The same checks apply to url and to callback_url.

Rejections return 422 with code invalid_url.

Rules

RuleRejected examples
http or https onlyfile:///etc/passwd, ftp://…, data:…
No loopback or reserved hostnameslocalhost, app.localhost
No internal-network suffixes*.local, *.internal, *.intranet, *.home.arpa
No cloud metadata endpointsmetadata.google.internal, metadata.goog
No private or reserved IPs127.0.0.1, 10.0.0.5, 192.168.1.10, 169.254.169.254, [::1]
No hostnames that resolve to private IPsa public name pointed at 10.x.x.x

The hostname is resolved and every A and AAAA record is checked; one private address among them rejects the url. A hostname that cannot be resolved is rejected too.

Redirects

The target is probed before the browser is started, and each redirect hop is re-checked, up to 5 hops. A public url that redirects to an internal address is refused mid-chain. The probe has a 3-second timeout and the target must answer with a status below 400.

This means a url that 404s, times out, or is behind a login wall returns 422 invalid_url rather than a screenshot of an error page.

Checked twice for async captures

For async callbacks, both the capture target and the callback host are re-validated when the job runs, not only when the request was accepted. A job may run minutes after validation, and DNS can be repointed in between.

Capturing private or authenticated pages

Ssnap cannot reach anything that is not publicly resolvable, and there is no parameter for credentials, cookies or headers. Options:

  • Expose a public, unguessable preview url (a signed token in the path) for the page you want captured.
  • Render the content to a temporary public page and capture that.
  • Allow the capture through a public staging host rather than an internal one.

An unguessable url is not an access control. Give it a short lifetime, and do not put anything behind it that would be harmful if the link were shared.

What happens during the render

Beyond the host checks, the render itself:

  • dismisses JavaScript dialogs, so alert() cannot hang a capture,
  • blocks googlesyndication.com, google-analytics.com and fonts.googleapis.com,
  • times out after 30 seconds.

Next