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
| Rule | Rejected examples |
|---|---|
http or https only | file:///etc/passwd, ftp://…, data:… |
| No loopback or reserved hostnames | localhost, app.localhost |
| No internal-network suffixes | *.local, *.internal, *.intranet, *.home.arpa |
| No cloud metadata endpoints | metadata.google.internal, metadata.goog |
| No private or reserved IPs | 127.0.0.1, 10.0.0.5, 192.168.1.10, 169.254.169.254, [::1] |
| No hostnames that resolve to private IPs | a 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.comandfonts.googleapis.com, - times out after 30 seconds.