# pyllyukko/user.js: Firefox hardening through a single preferences file

> pyllyukko/user.js is a Firefox user.js template that rewrites browser preferences at startup. It suits people who want hardened defaults without patching Firefox itself, and it will frustrate anyone who expects their about:config tweaks to stick.

**pyllyukko/user.js** — user.js -- Firefox configuration hardening

- Repository: https://github.com/pyllyukko/user.js
- Stars: 2,893 · Forks: 231
- Language: JavaScript
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/pyllyukko-user-js

## The problem pyllyukko/user.js addresses

Firefox ships with defaults chosen for a broad audience. Tightening those defaults by hand means opening about:config and flipping dozens of individual preferences, then remembering which ones you changed after an update resets them. pyllyukko/user.js turns that work into a file. The README lists the goals plainly: limit tracking through web analytics, harden the browser against known data disclosure or code execution vulnerabilities, limit persistent storage of sensitive data, harden cipher suites and protocols, and reduce the information available to browser fingerprinting. It also states an intent to disable features to shrink the attack surface while remaining usable for daily browsing.

That last goal is the interesting one, because the README immediately undercuts it. The default branch is described as "a default template with every possible hardening measure enforced," and a separate relaxed branch exists for people who want more usability. So the project ships two positions on the same trade-off and makes you pick. This is not a tool for someone who wants the browser to feel unchanged. It is for someone who wants the strictest configuration the maintainers could assemble, and who is willing to debug breakage when a site stops working.

## What the file actually does to Firefox

The mechanism is Firefox's own user.js convention, which the README links to the MozillaZine knowledge base entry for. Firefox reads user.js from the profile directory at startup and applies the preferences it contains. Because the file is re-read on each start, values written by user.js override anything you change later in about:config or the preferences dialogs. The README states this consequence directly: settings changed through about:config "will be reset to the user.js defined values after you restart Firefox."

That reset behaviour is the whole design. It guarantees the browser returns to the hardened state every session. It also means the profile-level install is not a starting point you then tune; the file is the source of truth. If a preference in the file breaks a site, you edit user.js, not about:config.

For deployments where you want the values to stick, the Makefile generates three alternative formats. The systemwide_user.js target rewrites user_pref( to pref( with sed, producing a file whose values act as defaults that can still be changed in about:config and persist across sessions. The locked_user.js target rewrites user_pref to lockPref, producing values that are locked and cannot be changed. The debian_locked.js target uses a regex to emit pref(name, value, locked) lines for Debian's packaging. Each target is a one-line sed transformation of the same user.js, which is a clean way to keep one source of truth across four delivery formats.

## Installing user.js in a Firefox profile

The README recommends creating a fresh Firefox profile or backing up your existing profile directory before installing, because the settings change browser behaviour substantially. To reach the Profile Manager, the README gives this command:

```bash
firefox --no-remote -P
```

Once you have a profile, download the file. The README offers three routes: cloning the repository, extracting the ZIP archive, or fetching user.js directly from the raw URL. Cloning is the simplest if you want to track changes:

```bash
git clone https://github.com/pyllyukko/user.js
```

Copy user.js into the profile directory. On Linux the README gives the path as:

```bash
cp user.js ~/.mozilla/firefox/XXXXXXXX.your_profile_name/user.js
```

The XXXXXXXX.your_profile_name segment is a placeholder for your actual profile folder name, not a literal path. On Windows the equivalent is %APPDATA%\Mozilla\Firefox\Profiles\XXXXXXXX.your_profile_name\user.js, and on OS X it is ~/Library/Application Support/Firefox/Profiles/XXXXXXXX.your_profile_name. The README also lists Android, Sailfish OS with Alien Dalvik, and portable Windows paths.

Restart Firefox. There is no installer, no extension, and no confirmation dialog. If you want to see the effect, open about:config and check a preference the file sets; the README's own advice is that manual changes there will not survive a restart, which is the quickest way to confirm the file is being read.

## System-wide installation and the make targets

The profile method is per-user. For a machine-wide or managed deployment, the Makefile generates a file you copy into the Firefox installation directory instead. The README names three targets:

```bash
make systemwide_user.js
make locked_user.js
make debian_locked.js
```

Each produces a differently transformed file. systemwide_user.js yields defaults that users can still override in about:config and that persist across sessions. locked_user.js yields values locked at profile creation that cannot be changed. debian_locked.js is Debian specific, and the README notes that users cannot override preferences with it, citing issue #415.

The destination depends on the platform. On Windows the README gives C:\Program Files (x86)\Mozilla Firefox\mozilla.cfg. On Linux it is /etc/firefox/syspref.js, with /etc/firefox/firefox.js for older versions, /etc/firefox-esr/firefox-esr.js on Debian, and /usr/lib/firefox/mozilla.cfg on Gentoo and Arch Linux, possibly under /usr/lib32/ or /usr/lib64/. On OS X it is /Applications/Firefox.app/Contents/Resources/mozilla.cfg.

For Windows, OS X, Gentoo and Arch Linux, the README adds a required step: create local-settings.js in the Firefox installation directory containing the two prefs that point Firefox at mozilla.cfg and set general.config.obscure_value to 0. Skipping that file means the generated configuration is never loaded, and the README does not describe a diagnostic for that failure beyond the file simply having no effect.

## Where this configuration gets in the way

The reset-on-restart behaviour is not a bug, but it is the most common source of confusion. If you set an exception in about:config for a site you use daily, the next restart discards it. Your options are editing user.js directly or switching to the system-wide target, which trades strictness for persistence. The README says as much: the profile method "prevents persistently changing settings you don't consider appropriate."

The README also carries a Known problems and limitations section, and its existence is the honest signal here. A configuration file that disables features to reduce attack surface will break sites that depend on those features, and the project's own documentation acknowledges that class of problem rather than claiming none exists. The default branch enforces every hardening measure, which is why the relaxed branch exists. If you need a browser that renders everything without intervention, this is the wrong tool, and the README's own branch structure says so.

The Android path deserves caution. The README lists /data/data/org.mozilla.firefox/files/mozilla/XXXXXXXX.your_profile_name and points to issue #14, which suggests the Android story is less settled than the desktop one. The README does not document rollback beyond the backup advice given up front.

## How it differs from Arkenfox and Betterfox

The related search data shows people comparing this project with Arkenfox and Betterfox, and the difference is one of posture rather than mechanism. All three ship a user.js that Firefox reads at startup, so the installation mechanics are the same. Where they diverge is what the file contains and how the maintainers frame it.

This project's README calls the default branch a template with "every possible hardening measure enforced" and offers a relaxed branch as the concession. The Makefile goes further than a single file: it can emit locked, system-wide and Debian-specific variants, and it includes diff targets that compare this user.js against Tor Browser's firefox.js profile, downloading the Tor Browser reference file with wget and extracting preference values with sed. That comparison tooling is unusual and points at the project's audience: people who want to know exactly how their configuration deviates from a hardened reference, not just that it is hardened.

If your priority is a configuration you rarely touch, the locked and system-wide targets are the distinguishing feature here. If your priority is a lighter touch that keeps more sites working, the relaxed branch is the intended answer, and the README points to it rather than pretending the default is universally comfortable.

## Maintenance, licence and upgrade cost

The repository is not archived, and the last push was on 2026-04-07. That is roughly six months before the date of writing, so treat the project as maintained but not fast-moving: the README records no releases, and the recent releases list is empty. There is no version number to pin, which matters for upgrades. You track the master branch, and an upgrade means replacing user.js and restarting Firefox. Any preference you added by editing the file is yours to reapply, and the README does not describe a merge or override mechanism for local modifications.

The Makefile's test targets are the quality gate you can run yourself. The tests target depends on node-acorn and shellcheck; test-acorn runs acorn --silent user.js to validate syntax, and test-shellcheck lints the shell scripts. Running these before copying a new user.js into a profile is a reasonable pre-upgrade check, and the README links a CI badge for the same workflow.

The licence is MIT, stated in the repository's LICENSE file and the repository metadata. MIT permits reuse and modification with the licence and copyright notice retained. That is a permissive arrangement, but it is not legal advice, and if you redistribute a modified user.js inside a product you should read the licence text yourself. There is no separate trademark or branding restriction mentioned in the repository files.

## Conclusion

Adopt pyllyukko/user.js if you want a documented, version-controlled set of Firefox preferences and accept that profile-level installation resets manual about:config changes on every restart. Do not adopt it if you need per-site exceptions you can set once and forget, or if you cannot test a fresh profile first. Before committing, check the relaxed branch for usability trade-offs, read the Known problems and limitations section in the README, and verify the path for your OS against the table rather than assuming the Linux path applies.

## FAQ

### Where does the user.js file go in Firefox?

It goes in your Firefox profile directory. The README gives ~/.mozilla/firefox/XXXXXXXX.your_profile_name/user.js on Linux, %APPDATA%\Mozilla\Firefox\Profiles\XXXXXXXX.your_profile_name\user.js on Windows, and ~/Library/Application Support/Firefox/Profiles/XXXXXXXX.your_profile_name on OS X, where the XXXXXXXX.your_profile_name part is your actual profile folder name.

### How do I install pyllyukko/user.js?

Clone the repository or download user.js, then copy the file into a Firefox profile directory, ideally a fresh profile or one you have backed up. Alternatively, run make with the systemwide_user.js, locked_user.js or debian_locked.js target and copy the generated file into the Firefox installation directory.

### Why do my about:config changes get reverted after restarting Firefox?

Because user.js is re-read at every startup. The README states that settings changed through about:config or the preferences dialogs are reset to the user.js defined values after restart, which is intentional. To make a change stick, edit user.js directly or use the system-wide installation target.

### What is the difference between pyllyukko/user.js and the relaxed branch?

The default branch is described in the README as a template with every possible hardening measure enforced. The relaxed branch is a variant that provides more usability, which means it trades some of those hardening measures for fewer broken sites.

### What licence does pyllyukko/user.js use?

The repository is licensed under MIT, per the LICENSE file and repository metadata. That permits reuse and modification provided the licence and copyright notice are retained.

## Sources

- [Issues](https://github.com/pyllyukko/user.js/issues)
- [License: MIT](https://github.com/pyllyukko/user.js/blob/master/LICENSE)
- [pyllyukko/user.js on GitHub](https://github.com/pyllyukko/user.js)
- [README](https://github.com/pyllyukko/user.js/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pyllyukko-user-js
