# Where Are Nitter User Preferences Stored? Cookie-Based Configuration Explained

> Discover where Nitter user preferences are stored. Learn how Nitter uses cookie-based configuration for your settings, ensuring a personalized experience.

- Repository: [Zed/nitter](https://github.com/zedeus/nitter)
- Tags: internals
- Published: 2026-09-04

---

**Nitter stores user preferences in HTTP cookies using the `savePref` template in `src/routes/router_utils.nim`, with fallback defaults generated from the configuration file via the `genDefaultPrefs` macro in `src/prefs_impl.nim`.**

[Nitter](https://github.com/zedeus/nitter) is a privacy-focused front-end for Twitter/X that operates without user accounts. When visitors toggle settings like themes, autoplay, or infinite scroll, those choices must persist across page loads. According to the zedeus/nitter source code, the application implements a three-tier preference hierarchy centered on browser cookies rather than server-side databases.

## How Nitter Stores Preferences

Unlike traditional web applications that persist settings in a database tied to user accounts, Nitter leverages the browser's cookie storage to maintain state anonymously. This approach aligns with the project's privacy-focused architecture.

### The Cookie-Based Architecture

Nitter writes preference changes to **HTTP cookies** immediately when a user modifies a setting. The `savePref` template in `src/routes/router_utils.nim` handles this operation, setting cookies with specific security flags including `httpOnly`, `secure`, and `sameSite` attributes based on the instance's HTTPS configuration.

When a request arrives, the `requestPrefs` template (also in `src/routes/router_utils.nim`) reads these cookies and constructs a `Prefs` object. This object represents the complete configuration state for that specific visitor.

### Configuration-Driven Defaults

If no cookies exist for a particular preference, Nitter falls back to **default values** compiled from the instance configuration. The `genDefaultPrefs` macro in `src/prefs_impl.nim` generates these defaults from the [`nitter.example.conf`](https://github.com/zedeus/nitter/blob/main/nitter.example.conf) file, ensuring every instance can customize its baseline user experience.

## Reading and Writing Preference Cookies

The preference system relies on two complementary operations: persisting changes to cookies and reconstructing the preference state on each request.

### Writing Cookies with `savePref`

The `savePref` template writes individual preferences as separate cookies with a 360-day expiration period (or immediate expiration when clearing). The implementation includes security hardening appropriate to the transport protocol:

```nim

# src/routes/router_utils.nim

template savePref*(pref, value: string; req: Request; expire = false) =
  setCookie(pref, value,
            daysForward(when expire: -10 else: 360),
            httpOnly = true, secure = cfg.useHttps,
            sameSite = if cfg.useHttps: None else: Lax,
            path = "/")

```

This template sets `httpOnly` to prevent JavaScript access, respects the instance's HTTPS settings for the `secure` flag, and applies `sameSite` policies to mitigate CSRF attacks.

### Reading Cookies with `getPrefs`

On each request, the `getPrefs` procedure in `src/prefs.nim` reconstructs the user's configuration by layering cookie values over the defaults:

```nim

# src/prefs.nim

proc getPrefs*(cookies, params: Table[string, string]): Prefs =
  result = defaultPrefs          # defaults from config

  genParsePrefs(cookies)        # overlay cookie values

  genParsePrefs(params)         # overlay URL "prefs" values

```

This procedure first populates the result with `defaultPrefs` (generated by the configuration macro), then overlays values from cookies, and finally applies any URL parameters for temporary overrides.

## The Preference Resolution Hierarchy

Nitter resolves preferences through a specific priority chain, allowing flexible configuration while maintaining user agency.

### URL Parameter Overrides

The system supports temporary preference changes via the **`prefs` query parameter**. The `applyUrlPrefs` mechanism (invoked through `genParsePrefs`) parses these parameters and overlays them on top of cookie values. This allows users to share links with specific viewing configurations without altering their saved preferences.

### Macro-Generated Defaults and Encoding

The `genDefaultPrefs` macro in `src/prefs_impl.nim` transforms the configuration file into compile-time Nim code, while `genEncodePrefs` handles the reverse operation—converting a `Prefs` object back into a query string for URL sharing:

```nim

# src/prefs_impl.nim

macro genEncodePrefs*(prefs): untyped =
  for pref in allPrefs():
    when pref.kind == checkbox:
      if prefs.`pref.name` != defaultPrefs.`pref.name`:
        encPairs.add pref.name & "=" & (if prefs.`pref.name`: "on" else: "")
    else:
      if prefs.`pref.name` != defaultPrefs.`pref.name`:
        encPairs.add pref.name & "=" & prefs.`pref.name`

```

This macro compares each preference against its default value, only encoding non-default settings to create clean, shareable URLs.

## Summary

- **Cookie Storage**: Nitter persists user preferences in HTTP cookies via the `savePref` template in `src/routes/router_utils.nim`, with 360-day expiration and security flags.
- **Default Generation**: Built-in defaults originate from [`nitter.example.conf`](https://github.com/zedeus/nitter/blob/main/nitter.example.conf) and compile into Nim code through the `genDefaultPrefs` macro in `src/prefs_impl.nim`.
- **Resolution Order**: The `getPrefs` procedure in `src/prefs.nim` applies configuration defaults first, then overlays cookie values, and finally processes URL parameters for temporary overrides.
- **Privacy Design**: By avoiding server-side storage and using `httpOnly` cookies, Nitter maintains its privacy-focused architecture without requiring user accounts.

## Frequently Asked Questions

### Are Nitter preferences stored server-side?

No. According to the zedeus/nitter source code, preferences are stored exclusively in browser cookies. The server maintains no database or session storage for individual user settings, aligning with Nitter's privacy-focused design that requires no user accounts.

### How long do Nitter preference cookies last?

Preference cookies persist for **360 days** by default, as defined in the `savePref` template in `src/routes/router_utils.nim`. When clearing preferences explicitly, the template sets an expiration of **10 days in the past** to immediately invalidate the cookie.

### Can I share Nitter preferences via URL?

Yes. The `genEncodePrefs` macro in `src/prefs_impl.nim` encodes non-default preferences into a `prefs` query parameter. When accessing a Nitter URL with this parameter, the `getPrefs` procedure applies these values as temporary overrides without modifying your stored cookie preferences.

### Where are the default preference values defined?

Default values are defined in the **[`nitter.example.conf`](https://github.com/zedeus/nitter/blob/main/nitter.example.conf)** configuration file. The `genDefaultPrefs` macro in `src/prefs_impl.nim` processes this file at compile time to generate the `defaultPrefs` constant used when no cookies are present.