# How Vaultwarden Icon Service Works: Internal vs External Fetching and Caching Options

> Explore Vaultwarden's icon service. Learn about internal vs external fetching, caching options, and how to configure TTLs, redirects, and CSP headers for optimal performance.

- Repository: [Daniel García/vaultwarden](https://github.com/dani-garcia/vaultwarden)
- Tags: internals
- Published: 2026-03-07

---

**Vaultwarden’s icon service fetches favicon images either internally by downloading them directly or externally by redirecting clients to third-party providers, with configurable caching TTLs, redirect HTTP codes, and Content-Security-Policy headers.**

The icon service supplies favicon images for domains stored in your Vaultwarden password vault. According to the Vaultwarden source code in `dani-garcia/vaultwarden`, the service operates through two distinct modes—internal fetching and external redirection—controlled by configuration values defined in [`src/config.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/config.rs) and implemented in [`src/api/icons.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/icons.rs).

## Configuring the Icon Service Mode

The `icon_service` configuration value defined in [`src/config.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/config.rs) (line 670) determines which operational mode is active. When set to **`internal`**, Vaultwarden handles all icon fetching directly. Any other value—such as `bitwarden`, `duckduckgo`, `google`, or a custom URL template containing exactly one `{}` placeholder—triggers the external redirection flow.

For external services, Vaultwarden generates the target URL using `_icon_service_url` (line 677), which substitutes the domain name into the template. The system also generates a Content-Security-Policy source list via `_icon_service_csp` (line 779) to ensure browsers can load the external resource.

## Request Routing and Handler Selection

The `routes()` function in [`src/api/icons.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/icons.rs) (lines 27-31) examines the configured `icon_service` at startup and registers the appropriate handler:

```rust
pub fn routes() -> Vec<Route> {
    match CONFIG.icon_service().as_str() {
        "internal" => routes![icon_internal],
        _ => routes![icon_external],
    }
}

```

This ensures that requests to `/domain/icon.png` route to either `icon_internal` or `icon_external` based on your configuration.

## External Redirect Flow

When operating in external mode, the `icon_external` function (lines 85-108) processes requests through the following steps:

1. **Domain validation** – Checks for malformed domains or entries in the block list, returning a negative cache response if invalid.
2. **URL generation** – Fills the template placeholder: `CONFIG._icon_service_url().replace("{}", domain)`.
3. **Redirect construction** – Uses the HTTP status code defined by `icon_redirect_code` (default 302).
4. **TTL caching** – Wraps the response in `Cached::ttl` with `icon_cache_ttl` to prevent repeated processing.

```rust
let url = CONFIG._icon_service_url().replace("{}", domain);
let redir = match CONFIG.icon_redirect_code() { ... };
Cached::ttl(redir, CONFIG.icon_cache_ttl(), true)

```

## Internal Fetching Flow

The `icon_internal` function (lines 110-138) handles direct icon downloads when `icon_service` is set to `internal`:

1. **Validation** – Performs domain and block list checks identical to the external path.
2. **Cache checking** – Calls `get_icon(domain).await`, which:
   - Checks for negative-cache markers (`*.miss`) via `icon_is_negcached` (lines 41-62).
   - Attempts to read positive cache entries via `get_cached_icon` and verifies freshness with `icon_is_expired` (lines 31-38).
3. **Download** – If cache misses and `disable_icon_download` is `false`, executes `download_icon` to fetch the resource.
4. **Storage** – Saves successful downloads via `save_icon` for future requests.
5. **Response** – Returns image bytes with the correct `Content-Type` wrapped in `Cached::ttl` using `icon_cache_ttl`.

```rust
Cached::ttl((ContentType::new("image", icon_type), icon), CONFIG.icon_cache_ttl(), true)

```

## Caching Configuration

Vaultwarden implements a two-tier caching system using OpenDAL storage under `PathType::IconCache`. The cache behavior is controlled by three key settings in [`src/config.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/config.rs):

- **`icon_cache_ttl`** (default 2,592,000 seconds / 30 days) – Duration that successfully downloaded icons remain valid. After expiry, Vaultwarden re-downloads the icon.
- **`icon_cache_negttl`** (default 259,200 seconds / 3 days) – Duration that failed download markers (`*.miss` files) remain cached, preventing repeated attempts on broken domains.
- **`icon_download_timeout`** (default 10 seconds) – Maximum time allowed for HTTP requests during icon fetching.

## Redirect Status Codes

The `icon_redirect_code` setting (line 81) supports four HTTP status codes for external redirects:

- **301** – Permanent redirect (legacy). Enables long-term client caching but may have compatibility issues with some Bitwarden clients.
- **302** – Temporary redirect (legacy). The default value ensuring maximum client compatibility.
- **307** – Temporary redirect with method preservation. Useful for testing new external services without breaking existing caches.
- **308** – Permanent redirect with method preservation. Preferred when the external service is permanent and you want clients to cache the redirect.

## Content-Security Policy Handling

When redirecting to external providers, Vaultwarden must update the Content-Security-Policy headers to allow the browser to fetch from the third-party origin. The `generate_icon_service_csp` function (lines 52-66) extracts the fixed portion of the URL template to build the CSP source list. For Google specifically, the function adds `*.gstatic.com` to accommodate secondary redirects performed by Google's infrastructure.

## Disabling Icon Downloads

Setting **`disable_icon_download`** to `true` (line 12) completely disables the internal fetcher. In this mode:

- Existing cached icons remain servable until their `icon_cache_ttl` expires.
- New requests for uncached icons return the fallback image (`fallback-icon.png`) without initiating external HTTP requests.
- The service effectively behaves like the external variant with an empty URL template.

## Configuration Examples

### Using DuckDuckGo External Service

Configure Vaultwarden to redirect icon requests to DuckDuckGo with permanent redirect caching:

```toml
ICON_SERVICE=duckduckgo
ICON_REDIRECT_CODE=308
ICON_CACHE_TTL=2592000
ICON_CACHE_NEGTTL=86400

```

Vaultwarden responds with a 308 redirect to URLs like `https://icons.duckduckgo.com/ip3/example.com.ico`, with CSP headers allowing `https://icons.duckduckgo.com`.

### Custom URL Template

Specify a corporate icon service with a custom template:

```toml
ICON_SERVICE="https://my-icons.mycorp.com/lookup/{}"

```

Requests for `/example.org/icon.png` receive a 302 redirect (default) to `https://my-icons.mycorp.com/lookup/example.org`.

### Internal Fetching with Extended Timeout

Maintain internal fetching with increased timeout for slower websites:

```toml
ICON_SERVICE=internal
DISABLE_ICON_DOWNLOAD=false
ICON_DOWNLOAD_TIMEOUT=15
ICON_CACHE_TTL=2592000

```

### Disabling Downloads to Reduce External Traffic

Prevent all external HTTP requests while serving existing cache:

```toml
DISABLE_ICON_DOWNLOAD=true
ICON_CACHE_TTL=0

```

## Summary

- Vaultwarden’s icon service supports **two operational modes**: `internal` (direct download) and external (HTTP redirect).
- The **`icon_service`** configuration value in [`src/config.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/config.rs) controls the mode, accepting either `internal` or a URL template for third-party providers.
- **Caching** uses positive TTL (`icon_cache_ttl`, default 30 days) for successful icons and negative TTL (`icon_cache_negttl`, default 3 days) for failed attempts, stored via OpenDAL.
- External redirects support **four HTTP status codes** (301, 302, 307, 308) configurable via `icon_redirect_code`.
- **CSP headers** are automatically generated for external services to allow browser fetching, with special handling for Google's secondary redirects.
- Setting **`disable_icon_download`** to `true` blocks all outbound icon traffic while continuing to serve existing cached entries.

## Frequently Asked Questions

### What is the default icon service mode in Vaultwarden?

The default mode is **`internal`**, configured in [`src/config.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/config.rs) (line 670). In this mode, Vaultwarden fetches icons directly from the target websites, parses HTML for favicon links, and caches the results locally using OpenDAL storage. This eliminates reliance on third-party services but requires outbound HTTP connections from your Vaultwarden instance.

### How does Vaultwarden handle icon caching?

Vaultwarden implements a **dual-layer caching strategy** via [`src/api/icons.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/icons.rs). Successful downloads are stored with a positive TTL defined by `icon_cache_ttl` (default 2,592,000 seconds), while failed attempts create `*.miss` marker files cached for `icon_cache_negttl` (default 259,200 seconds). The `get_cached_icon` function checks these markers before attempting downloads, and `icon_is_expired` (lines 31-38) determines whether cached entries need refreshing.

### Can I use a custom icon provider with Vaultwarden?

Yes. Set **`icon_service`** to any string containing exactly one `{}` placeholder, such as `https://my-cdn.com/icons/{}.png`. Vaultwarden treats this as a custom external provider, substituting the requested domain for `{}` and redirecting clients to the resulting URL. The `generate_icon_service_csp` function automatically extracts the base domain for Content-Security-Policy headers.

### What happens when icon downloads are disabled?

When **`disable_icon_download`** is set to `true` (line 12), Vaultwarden skips the `download_icon` call in [`src/api/icons.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/icons.rs). The service returns existing cached icons until they expire per `icon_cache_ttl`, after which requests receive the fallback icon (`fallback-icon.png`). This configuration ensures zero outbound HTTP traffic for icon fetching while maintaining functionality for previously cached entries.