imageproxy

repository·main·Indexed 26 days ago

https://github.com/willnorris/imageproxy

A caching image proxy server written in Go that enables dynamic image resizing, cropping, and rotation. It supports multiple image formats and various caching backends including S3, Redis, local storage, GCS, and Azure Storage. The server can be deployed as a standalone binary, a Docker container, or as an embedded Caddy module. It includes security features such as HMAC-SHA256 signed URLs, referrer lists, and host allow/deny lists to prevent unauthorized use.

Tokens
6.8K
Snippets
16
Records
46
Agent score
82%

What's inside imageproxy

  1. Understand imageproxy URL structure

    main

    imageproxy uses a specific URL format to apply transformations to remote images. The structure is:

    http://localhost/{options}/{remote_url}

    • {options}: A comma-delimited list of parameters for cropping, resizing, rotation, flipping, etc. Parameters can be in any order, and duplicates overwrite previous values.
    • {remote_url}: The URL of the original image. It can be provided in three ways:
      1. Plain text: Any query string in the proxy URL is treated as part of the remote URL. Example: http://localhost/x/http://example.com/?id=1 targets http://example.com/?id=1.
      2. Percent-encoded: The full URL must be encoded. Query strings on the proxy URL are NOT included in the remote URL. These must be absolute URLs.
      3. Base64 encoded (URL safe, no padding): The full URL must be encoded. Query strings on the proxy URL are NOT included in the remote URL. These can be relative URLs if a default base URL is configured.
  2. Proxy requests to imageproxy using nginx

    main

    To route requests to an imageproxy instance running at /api/imageproxy/ using nginx, use the proxy_pass directive. Depending on your configuration, you may need to use the ^~ modifier to control precedence:

    location /api/imageproxy/ {
      proxy_pass http://localhost:4593/;
    }

    Or with precedence control:

    location ^~ /api/imageproxy/ {
      proxy_pass http://localhost:4593/;
    }
  3. Implement and register a custom plugin

    main

    To create a custom plugin for imageproxy, you must implement one or more of the project's extension interfaces (such as RequestAuthorizer, ResponseAuthorizer, or ImageTransformer) and register the plugin using an init function that calls imageproxy.RegisterPlugin.

    Plugins are loaded by importing their package. They are also responsible for registering any additional command line flags they require and managing their own global state.

  4. Deploy imageproxy using Docker

    main

    You can run imageproxy using the official Docker image available at ghcr.io/willnorris/imageproxy.

    When running in a container with a bind-mounted on-disk cache, ensure the container user has write permissions to the mounted host directory. For containerized environments, it is recommended to use environment variables for configuration.

    docker run -p 8080:8080 ghcr.io/willnorris/imageproxy -addr 0.0.0.0:8080
  5. Canonicalize transformation options for signing

    main

    When signing a request that includes transformation options, you must follow these rules to ensure the signature matches what imageproxy expects:

    1. Canonicalize the size option: The size option must be in the format {width}x{height}. Use 0 if a value is not specified (e.g., 0x500 for only height, or 0x0 for no size transformation).
    2. Sort options: All transformation options (excluding the signature itself) must be sorted in lexicographical order.
    3. Append as fragment: The resulting canonical string of options should be appended to the remote URL as a URL fragment (#).

    Example:

    • Remote URL: http://example.com/image.jpg
    • Requested options: 100 (size), r90 (rotation), q75 (quality)
    • Canonical size: 100x100
    • Sorted options: 100x100,q75,r90
    • String to sign: http://example.com/image.jpg#100x100,q75,r90
  6. Generate signed requests for imageproxy

    main

    To prevent abuse when proxying images from arbitrary remote hosts, you can use signatures. imageproxy uses HMAC-SHA256 with a secret key (provided at startup) to validate requests.

    There are two ways to sign requests:

    1. Sign the remote URL only: Any transformation can be requested without changing the signature. This is not recommended as it allows users to request any transformation.
    2. Sign the remote URL and transformation options: The signature is calculated based on the combination of the remote URL and the requested options. This is the recommended method.

    The signature must be URL-safe base64 encoded and provided as the s option in the imageproxy request URL.

  7. Install and run imageproxy

    main

    Install the imageproxy binary using go install. Ensure $GOPATH/bin is in your $PATH. Running imageproxy without flags starts the proxy on port 8080 with no caching and no host restrictions.

    go install willnorris.com/go/imageproxy/cmd/imageproxy@latest
    imageproxy
  8. Use the embedded imageproxy Caddy module

    main

    You can run imageproxy embedded in Caddy using the imageproxy module. This requires a custom build of Caddy that includes the module. Configure it in your Caddyfile as follows:

    @imageproxy path /api/imageproxy/*
    handle @imageproxy {
      uri replace /api/imageproxy/ /
    
      imageproxy {
        cache /data/imageproxy-cache
        default_base_url {$IMAGEPROXY_BASEURL}
        allow_hosts {$IMAGEPROXY_ALLOWHOSTS}
        signature_key {$IMAGEPROXY_SIGNATUREKEY}
      }
    }
  9. Proxy requests to imageproxy using Caddy

    main

    To proxy requests to a standalone imageproxy instance in Caddy, use the reverse_proxy directive and strip the prefix using uri replace:

    @imageproxy path /api/imageproxy/*
    handle @imageproxy {
      uri replace /api/imageproxy/ /
      reverse_proxy http://localhost:4593
    }
  10. Build and deploy imageproxy as a Go binary

    main

    You can build and deploy imageproxy following standard Go deployment procedures:

    1. Build the binary: go build willnorris.com/go/imageproxy/cmd/imageproxy
    2. Copy the binary to /usr/local/bin
    3. Copy etc/imageproxy.service to /lib/systemd/system and enable it using systemctl.
    go build willnorris.com/go/imageproxy/cmd/imageproxy
  11. Secure requests with Signed URLs

    main

    Instead of a whitelist, require HMAC-SHA256 signatures for requests using the -signatureKey flag.

    • Signatures are generated by: base64urlencode(hmac.New(sha256, <key>).digest(<remote_url>)).
    • If the key starts with @, the value is treated as a path to a file containing the key.
    • Multiple keys can be provided via repeated flags or space-separated lists to support rotation.
    • To limit URL validity, use the vu (valid until) option with a Unix timestamp in the URL.

    If both a whitelist (-allowHosts) and -signatureKey are provided, a request is valid if it matches either.