Guides

How to certify the content of a website with Urlbox

Need proof that a screenshot was taken at a given time and isn't altered? Our new `certify` option can do it.

Arnold Cubici-Jones
Arnold Cubici-Jones
April 15, 2025

We’re excited to introduce a new feature to our API: Certified Screenshots. This enhancement provides cryptographic proof that a screenshot was taken with a specific set of options at a given time, giving more authenticity and integrity to your renders.

Why is this useful?

Whether it's for regulatory archiving, brand monitoring, or digital forensics, this provides more authentic, trustworthy immutable web content. Here are some concrete use cases:

  • Regulatory Compliance – You may need to prove you displayed certain policies, disclosures, or terms at a specific time.
  • Preserving Online Content – Articles, social media posts, or public statements may be deleted or altered; a certified render ensures the truth remains captured.
  • Monitoring Competitor Activity – You can track and preserve changes to websites for compliance or legal disputes.

How It Works

When you make a render request, add certify: true to your options. In response, we return all the values we did before, plus:

  • A timestamp of when your screenshot was taken.
  • A cryptographic hash of the options, the rendered file, and the timestamp.
  • The options used in the hash (these may differ slightly from your inputted options).

Storing these three allow you to verify the integrity of the screenshot at any time.

Some of the options you provide may generate additional options automatically. This is why the hashed options might differ slightly from what you originally submitted.

For example, we’ve recently updated Urlbox to use pre-signed URLs by default when storing renders in S3, to enhance security. When you include use_s3=true, we generate a s3_presigned_url—a derived option that becomes part of your final configuration.

Rest assured, using S3 and certified renders is just as safe. Pre-signed URLs are intentionally ephemeral, meaning they expire after a short time. Even if someone gained access to your hash and could decode it, the pre-signed URL would likely have already expired, making it useless for uploading to your S3 bucket.

Note - This approach works with all render formats—except for video formats and when using html=<h1>Hello World!</h1> instead of a url=https://example.com. It is not compatible with options that significantly alter the page’s structure or content, as that goes against the nature of this feature. Additionally, any options that might contain sensitive information are not compatible, to ensure privacy and security (except for use_s3).

To confirm that a screenshot hasn't been altered, you can:

  1. Take the file, the timestamp, and the returned hashedOptions and concatenate them together with a period between each one e.g. "{file}.{timestamp}.{hashedOptions}".
  2. Hash the concatenated string using the sha256 hashing algorithm.
  3. Compare the hash to the one provided by our API response.
  4. If the hashes match, the screenshot is verified as authentic and unaltered.

Example

You can make a request using your favoured method. Here's an example with Curl and Postman:

curl --location 'https://api.urlbox.com/v1/render/sync' \
--header 'Authorization: Bearer {{YOUR_URLBOX_SECRET_HERE}}' \
--header 'Content-Type: application/json' \
--data '{
    "url": "https://theWebsiteYouWantToRender.com",
    "certify": true
}'
Run In Postman

The returned JSON should look familiar, with the addition of the certifiedHash, timestamp and hashedOptions:

{
  "renderUrl":"https://yourStorageLocation/yourRender.pdf",
  "size": 297573,
  "timestamp": "Mon, 17 Mar 2025 10:30:25 GMT",
  "certifiedHash": "b64b8...",
  "hashedOptions": {
    "url": "https://theWebsiteYouWantToRender.com",
    "certify": true
  }
}

To verify this, we recommend using a callable script for manual verification, to simplify the process for you. Feel free to write your own code which performs the steps above.

The example below is based on a macOS/Linux environment with a shell script. Most hashing tools compatible with the sha256 algorithm should also work.

Steps to verify:

  1. Copy the below, save it as verify.sh.
  2. Give it permission to run by executing chmod +x verify.sh in the command line.
  3. Ensure you've got jq available for JSON formatting.
  4. Take the renderUrl from the JSON response you received earlier, and save that render locally.
  5. Run ./verify.sh /path/to/the/downloaded/file.pdf in the command line.

You'll be prompted for the JSON response from the API. Paste it with Ctrl+V and hit Ctrl+D twice to confirm.

The response should show you the computed hash from the shell script, the certified hash from the JSON response, and whether they matched. If they match, then you have a certified screenshot!

#!/bin/bash
 
# Check that we passed in the downloaded file path
if [[ $# -lt 1 ]]; then
  echo "Error: Missing local file path."
  echo "Usage: ./verify.sh <localFilePath>"
  exit 1
fi
 
FILE_PATH="$1"
 
# Ensure the file exists
if [[ ! -f "$FILE_PATH" ]]; then
  echo "Error: File '$FILE_PATH' not found."
  exit 1
fi
 
# Prompt to paste render response JSON
echo "Paste JSON payload and press Ctrl+D twice when done:"
JSON_INPUT=$(jq -nR '[inputs] | join("\n")' | jq -r '. | fromjson' 2>/dev/null)
 
# Validate the JSON
if [[ -z "$JSON_INPUT" || $(echo "$JSON_INPUT" | jq empty 2>&1) ]]; then
  echo "Error: Invalid JSON provided."
  exit 1
fi
 
# Extract values from the JSON
RENDER_TIMESTAMP=$(echo "$JSON_INPUT" | jq -r '.timestamp')
CERTIFIED_HASH=$(echo "$JSON_INPUT" | jq -r '.certifiedHash')
HASHED_OPTIONS_JSON=$(echo "$JSON_INPUT" | jq -cS '.hashedOptions')
 
# Compute SHA-256 hash of the file
BUFFER_HASH=$(shasum -a 256 "$FILE_PATH" | awk '{print $1}')
 
echo
echo "FILE: $FILE_PATH"
echo "BUFFER_HASH: $BUFFER_HASH"
echo "OPTIONS: $HASHED_OPTIONS_JSON"
echo "TIMESTAMP: $RENDER_TIMESTAMP"
echo
 
# Compute the final hash to compare against the certified hash
FINAL_HASH=$(echo -n "$BUFFER_HASH.$RENDER_TIMESTAMP.$HASHED_OPTIONS_JSON" | shasum -a 256 | awk '{print $1}')
 
# Compare hashes
MATCH_RESULT="false"
if [[ "$FINAL_HASH" == "$CERTIFIED_HASH" ]]; then
  MATCH_RESULT="true"
fi
 
# Output results in JSON format
jq -n \
  --arg computedHash "$FINAL_HASH" \
  --arg certifiedHash "$CERTIFIED_HASH" \
  --arg match "$MATCH_RESULT" \
  '{computedHash: $computedHash, certifiedHash: $certifiedHash, match: $match}'
 

Here's what an example Terminal Execution should look like:

 
./verify.sh pdf.pdf
 
Paste JSON payload and press Ctrl+D twice when done:
{
    "renderUrl": "https://someRenderUrl/file.pdf",
    "size": 3966457,
    "timestamp": "Mon, 17 Mar 2025 12:22:23 GMT",
    "certifiedHash": "8b707...",
    "hashedOptions": {
        "url": "https://theWebsiteYouWantToRender.com",
        "format": "pdf",
        "certify": true
    }
}
 
 
FILE: pdf.pdf
BUFFER_HASH: 5780000...
OPTIONS: {"certify":true,"format":"pdf","url":"https://theWebsiteYouWantToRender.com"}
TIMESTAMP: Mon, 17 Mar 2025 12:22:23 GMT
 
{
  "computedHash": "8b707...",
  "certifiedHash": "8b707...",
  "match": "true"
}

Room to Improve

We recognize that while it adds a layer of integrity verification, there are potential weaknesses. If you require stronger authenticity guarantees please reach out to us. We’re open to feedback and eager to evolve this feature to meet higher authentication and legal standards.

Additionally, if you're struggling to get started, or to verify your renders, please do reach out to us.

Happy rendering!

Free Trial

Ready to start rendering?

Designers, law firms and infrastructure engineers trust Urlbox to accurately and securely convert HTML to images at scale. Experience it for yourself.

7 day free trial.No credit card required.