Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c1af45a
Implement fromScratch design
Timendus Jul 30, 2024
c7e3fcc
Add some documentation
Timendus Jul 30, 2024
a6e5241
enable new design in docker (no alpine yet)
sstidl Oct 19, 2024
66e967a
Merge branch 'master' of github.com:librespeed/speedtest into newdesign
sstidl Oct 19, 2024
4b6a8b8
merge fixed docker images
sstidl Oct 26, 2024
e8c8ac8
alpine docker added new design
sstidl Oct 26, 2024
74644b0
Merge branch 'master' of https://github.com/librespeed/speedtest into…
sstidl Oct 26, 2024
a02f4e3
fix #685
sstidl Nov 30, 2024
0642af8
Implement fromScratch design
Timendus Dec 1, 2024
1dc40d7
Add some documentation
Timendus Dec 1, 2024
853cb4f
enable new design in docker (no alpine yet)
sstidl Dec 1, 2024
96b0261
alpine docker added new design
sstidl Dec 1, 2024
cc4c2f8
fix #685
sstidl Dec 1, 2024
018c696
Merge branch 'newdesign' of https://github.com/librespeed/speedtest i…
stefanstidlffg Dec 1, 2024
83f7491
fix database permissions alpine, remove baby
sstidl Dec 1, 2024
b345343
Merge remote-tracking branch 'origin/master' into newdesign
sstidl Dec 1, 2024
13f6c49
Merge commit 'dd40a3a4937d88fdc2337adb190fb9d091e849a4' into newdesign
sstidl Dec 29, 2024
f06187a
Merge branch 'fix-docker-images' into newdesign
sstidl Dec 29, 2024
dda7841
hide serverselector on only one server
sstidl Dec 29, 2024
4fc0932
Update frontend/styling/server-selector.css
sstidl Apr 22, 2025
7a11454
Merge branch 'master' into newdesign
sstidl Dec 6, 2025
b1a5cea
Merge commit 'fc2dc5125d60280066e368417534bfe762068156' into newdesign
stefanstidlffg Dec 6, 2025
61ec779
fix alpine image again
stefanstidlffg Dec 6, 2025
ec7b0ab
Merge remote-tracking branch 'orig/master' into newdesign
stefanstidlffg Dec 6, 2025
b1111e5
adjust settings.json in entrypoint
sstidl Dec 7, 2025
3a0e6b3
Update frontend/javascript/index.js
sstidl Dec 7, 2025
235fa63
Add feature switch for new design via config file, URL parameters, an…
Copilot Dec 29, 2025
cb79f72
add armv7
stefanstidlffg Dec 29, 2025
0570377
reformat
stefanstidlffg Dec 29, 2025
119eb1f
Add GDPR_EMAIL environment variable for Docker deployments (#743)
Copilot Dec 29, 2025
48af9e8
cleanup old EMAIL ENV Var
sstidl Dec 29, 2025
a41566f
fix: line break in html prevented sed replacement
sstidl Dec 29, 2025
007a050
version 6.0.0pre1
sstidl Dec 30, 2025
01d7845
test: add mssql docker compose tests
Feb 7, 2026
27acb2d
Update Speedtest screen recording link in README
sstidl Mar 16, 2026
eda4148
Merge remote-tracking branch 'origin/master' into newdesign
Mar 18, 2026
1c7c44e
Filter unreachable servers from selector (newdesign UI) (#769)
sstidl Mar 18, 2026
debd40b
fix: server.json handling
Mar 18, 2026
fdb1681
Use server-list.json in classic frontend by default
Mar 18, 2026
8449b94
Add configurable server list URLs to frontend and Docker
Mar 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,15 @@
"addEventListener": "readonly"
},
"rules": {
"no-unused-vars": ["warn", { "args": "none" }],
"no-unused-vars": [
"warn",
{
"args": "none"
}
],
"no-console": "off",
"no-empty": "warn",
"no-undef": "warn",
"no-const-assign": "error"
}
}
}
2 changes: 1 addition & 1 deletion .prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@
"arrowParens": "avoid",
"endOfLine": "lf",
"htmlWhitespaceSensitivity": "ignore"
}
}
96 changes: 96 additions & 0 deletions DESIGN_SWITCH.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Design Feature Switch

LibreSpeed now supports switching between the classic design and the new modern design.

## Default Behavior

By default, LibreSpeed uses the **classic design** (located in `index-classic.html`).

## Architecture

### File Structure (Non-Docker)
- **`index.html`** - Entry point (lightweight switcher)
- **`index-classic.html`** - Classic design at root
- **`index-modern.html`** - Modern design at root (references assets in subdirectories)
- **`frontend/`** - Directory containing modern design assets (CSS, JS, images, fonts) - kept for non-Docker deployments

### File Structure (Docker)
In Docker deployments, the frontend assets are flattened to root-level subdirectories:
- **`index.html`** - Entry point (lightweight switcher)
- **`index-classic.html`** - Classic design
- **`index-modern.html`** - Modern design
- **`styling/`** - CSS files for modern design
- **`javascript/`** - JS files for modern design
- **`images/`** - Images for modern design
- **`fonts/`** - Fonts for modern design
- **No `frontend/` directory** - Assets are copied directly to root subdirectories

### Benefits of Root-Level Design Files
✅ Both designs at same level - no path confusion
✅ `results/` accessible from both designs with same relative path
✅ `backend/` accessible from both designs with same relative path
✅ No subdirectory nesting issues
✅ Clean separation of concerns
✅ Docker containers have no `frontend/` parent directory

## Browser Compatibility

The feature switch uses modern JavaScript features (URLSearchParams, fetch API). It is compatible with all modern browsers. The new design itself requires modern browser features and has no backwards compatibility with older browsers (see `frontend/README.md`).

## Enabling the New Design

There are two ways to enable the new design:

### Method 1: Configuration File (Persistent)

Edit the `config.json` file in the root directory and set `useNewDesign` to `true`:

```json
{
"useNewDesign": true
}
```

This will make the new design the default for all users visiting your site.

### Method 2: URL Parameter (Temporary Override)

You can override the configuration by adding a URL parameter:

- To use the new design: `http://yoursite.com/?design=new`
- To use the old design: `http://yoursite.com/?design=old`

URL parameters take precedence over the configuration file, making them useful for testing or allowing users to choose their preferred design.

## Design Locations

### Non-Docker Deployments
- **Entry Point**: Root `index.html` file (lightweight redirect page)
- **Old Design**: `index-classic.html` at root
- **New Design**: `index-modern.html` at root (references assets in `frontend/` subdirectory)
- **Assets**: Frontend assets (CSS, JS, images, fonts) in `frontend/` subdirectory

### Docker Deployments
- **Entry Point**: Root `index.html` file (lightweight redirect page)
- **Old Design**: `index-classic.html` at root
- **New Design**: `index-modern.html` at root (references assets in root subdirectories)
- **Assets**: Frontend assets copied directly to root subdirectories (`styling/`, `javascript/`, `images/`, `fonts/`)
- **No `frontend/` directory** - Assets are flattened to root level

Both designs are at the same directory level, ensuring that relative paths to shared resources like `backend/` and `results/` work correctly for both.

## Technical Details

The feature switch is implemented in `design-switch.js`, which is loaded by the root `index.html`. It checks:

1. First, URL parameters (`?design=new` or `?design=old`)
2. Then, the `config.json` configuration file
3. Redirects to either `index-classic.html` or `index-modern.html`

Both design HTML files are at the root level, eliminating path issues.

### Non-Docker
The modern design references assets from the `frontend/` subdirectory (e.g., `frontend/styling/index.css`), while both designs can access shared resources like `backend/` and `results/` using the same relative paths.

### Docker
In Docker deployments, the `frontend/` directory is flattened during container startup. Assets are copied directly to root-level subdirectories (`styling/`, `javascript/`, `images/`, `fonts/`), and `index-modern.html` references these root-level paths. This eliminates the `frontend/` parent directory in the container.
8 changes: 6 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,30 @@

# Copy sources
COPY backend/ /speedtest/backend
COPY frontend/ /speedtest/frontend

COPY results/*.php /speedtest/results/
COPY results/*.ttf /speedtest/results/

COPY *.js /speedtest/
COPY index.html /speedtest/
COPY index-classic.html /speedtest/
COPY index-modern.html /speedtest/
COPY config.json /speedtest/
COPY favicon.ico /speedtest/

COPY docker/servers.json /servers.json

COPY docker/*.php /speedtest/
COPY docker/entrypoint.sh /

# Prepare default environment variables
ENV TITLE=LibreSpeed
ENV MODE=standalone
ENV PASSWORD=password

Check warning on line 36 in Dockerfile

View workflow job for this annotation

GitHub Actions / build (./Dockerfile, ghcr.io/librespeed/speedtest)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "PASSWORD") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
ENV TELEMETRY=false
ENV ENABLE_ID_OBFUSCATION=false
ENV REDACT_IP_ADDRESSES=false
ENV WEBPORT=8080
ENV USE_NEW_DESIGN=false

# https://httpd.apache.org/docs/2.4/stopping.html#gracefulstop
STOPSIGNAL SIGWINCH
Expand Down
8 changes: 6 additions & 2 deletions Dockerfile.alpine
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,30 @@

# Copy sources
COPY backend/ /speedtest/backend
COPY frontend/ /speedtest/frontend

COPY results/*.php /speedtest/results/
COPY results/*.ttf /speedtest/results/

COPY *.js /speedtest/
COPY index.html /speedtest/
COPY index-classic.html /speedtest/
COPY index-modern.html /speedtest/
COPY config.json /speedtest/
COPY favicon.ico /speedtest/

COPY docker/servers.json /servers.json

COPY docker/*.php /speedtest/
COPY docker/entrypoint.sh /

# Prepare default environment variables
ENV TITLE=LibreSpeed
ENV MODE=standalone
ENV PASSWORD=password

Check warning on line 50 in Dockerfile.alpine

View workflow job for this annotation

GitHub Actions / build (./Dockerfile.alpine, ghcr.io/librespeed/speedtest, -alpine)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "PASSWORD") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
ENV TELEMETRY=false
ENV ENABLE_ID_OBFUSCATION=false
ENV REDACT_IP_ADDRESSES=false
ENV WEBPORT=8080
ENV USE_NEW_DESIGN=false

# https://httpd.apache.org/docs/2.4/stopping.html#gracefulstop
STOPSIGNAL SIGWINCH
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Works with mobile versions too.
* Results sharing (optional)
* Multiple Points of Test (optional)

![Screenrecording of a running Speedtest](https://speedtest.fdossena.com/mpot_v6.gif)
![Screenrecording of a running Speedtest](https://speedtest.fdossena.com/mpot_v7.gif)

## Server requirements

Expand Down
3 changes: 3 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"useNewDesign": false
}
72 changes: 72 additions & 0 deletions design-switch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Feature switch for enabling the new LibreSpeed design
*
* This script checks for:
* 1. URL parameter: ?design=new or ?design=old
* 2. Configuration file: config.json with useNewDesign flag
*
* Default behavior: Shows the old design
*
* Note: This script is only loaded on the root index.html
*/
(function () {
'use strict';

// Don't run this script if we're already on a specific design page
// This prevents infinite redirect loops
const currentPath = window.location.pathname;
if (currentPath.includes('index-classic.html') || currentPath.includes('index-modern.html')) {
return;
}

// Check URL parameters first (they override config)
const urlParams = new URLSearchParams(window.location.search);
const designParam = urlParams.get('design');

if (designParam === 'new') {
redirectToNewDesign();
return;
}

if (designParam === 'old' || designParam === 'classic') {
redirectToOldDesign();
return;
}

// Check config.json for design preference
try {
const xhr = new XMLHttpRequest();
// Use a synchronous request to prevent a flash of the old design before redirecting
xhr.open('GET', 'config.json', false);
xhr.send(null);

// Check for a successful response, but not 304 Not Modified, which can have an empty response body
if (xhr.status >= 200 && xhr.status < 300) {
const config = JSON.parse(xhr.responseText);
if (config.useNewDesign === true) {
redirectToNewDesign();
} else {
redirectToOldDesign();
}
} else {
// Config not found or error - default to old design
redirectToOldDesign();
}
} catch (error) {
// If there's any error (e.g., network, JSON parse), default to old design
console.log('Using default (old) design:', error.message || 'config error');
redirectToOldDesign();
}

function redirectToNewDesign() {
// Preserve any URL parameters when redirecting
const currentParams = window.location.search;
window.location.href = 'index-modern.html' + currentParams;
}

function redirectToOldDesign() {
// Preserve any URL parameters when redirecting
const currentParams = window.location.search;
window.location.href = 'index-classic.html' + currentParams;
}
})();
23 changes: 2 additions & 21 deletions doc.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# LibreSpeed

> by Federico Dossena
> Version 5.5.1
> Version 6.0.0pre1
> [https://github.com/librespeed/speedtest/](https://github.com/librespeed/speedtest/)

## Introduction
Expand Down Expand Up @@ -83,23 +83,7 @@ Log into your database using phpMyAdmin or a similar software and create a new d

Open `results/telemetry_settings.php` in a text editor. Set `$db_type` to either `mysql`,`postgresql`, `mssql` or `sqlite`.

If you chose to use SQLite, the default configuration stores the database at `__DIR__ . '/../../speedtest_telemetry.db'`, which places it two directories up from the `results/` folder. This is designed to keep the database **outside your web-accessible directory** for security.

**Critical Security Requirements**:
1. **Web Server Configuration**: Configure your web server's document root to point to the application directory, NOT its parent. For example:
- Install application files to: `/var/www/speedtest/`
- Set Apache/nginx document root to: `/var/www/speedtest/` (NOT `/var/www/`)
- Database will be at: `/var/www/speedtest_telemetry.db` (outside document root, not web-accessible)

2. **Alternative: Use Absolute Path**: For maximum security, especially if you cannot control the document root configuration, modify `$Sqlite_db_file` in `results/telemetry_settings.php` to use an absolute path completely outside your web directories:
```php
$Sqlite_db_file = '/var/lib/speedtest/speedtest_telemetry.db'; // or /opt/speedtest_data/speedtest_telemetry.db
```
Ensure the web server has write permissions to this directory.

3. **Verification**: After running at least one speed test or accessing `sanitycheck.php` (which creates the database), try accessing `http://yourserver/speedtest_telemetry.db` in a browser - you should get a 404 error. If the file downloads, your configuration is insecure. Note: The database file won't exist until the first test is recorded, so you'll get a 404 initially even with correct configuration.

SQLite doesn't require any additional configuration beyond setting a secure path and ensuring proper permissions.
If you chose to use SQLite, you might want to change `$Sqlite_db_file` to another path where you want the database to be stored. Just make sure that the file cannot be downloaded by users. Sqlite doesn't require any additional configuration, you can skip the rest of this section.

If you chose to use MySQL, you must set your database credentials:

Expand Down Expand Up @@ -872,9 +856,6 @@ s.setParameter("test_order","P_D_U");

This will point to our static files and set the test to only do ping/jitter, download and upload tests.

There is also an example to achieve this with nginx backend here:
https://github.com/librespeed/speedtest/issues/375#issuecomment-3769211254

## Troubleshooting

These are the most common issues reported by users, and how to fix them. If you still need help, contact me at [info@fdossena.com](mailto:info@fdossena.com).
Expand Down
12 changes: 10 additions & 2 deletions doc_docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ services:
#ENABLE_ID_OBFUSCATION: "false"
#REDACT_IP_ADDRESSES: "false"
#PASSWORD:
#EMAIL:
#GDPR_EMAIL: "privacy@example.com"
#DISABLE_IPINFO: "false"
#IPINFO_APIKEY: "your api key"
#DISTANCE: "km"
Expand All @@ -57,6 +57,8 @@ The test can be accessed on port 80.
Here's a list of additional environment variables available in this mode:

* __`TITLE`__: Title of your speed test. Default value: `LibreSpeed`
* __`USE_NEW_DESIGN`__: When set to `true`, enables the new modern frontend design. When set to `false` (default), uses the classic design. The design can also be switched using URL parameters (`?design=new` or `?design=old`). Default value: `false`
* __`SERVER_LIST_URL`__: When set, both frontend designs load their server list from this URL instead of the generated or mounted `server-list.json`. This is useful if you want the containerized frontend to consume a remote shared server list.
* __`TELEMETRY`__: Whether to enable telemetry or not. If enabled, you maybe want your data to be persisted. See below. Default value: `false`
* __`ENABLE_ID_OBFUSCATION`__: When set to true with telemetry enabled, test IDs are obfuscated, to avoid exposing the database internal sequential IDs. Default value: `false`
* __`OBFUSCATION_SALT`__: The salt string that is used to obfuscate the test IDs. The format shoud be a 2 byte hex string (e.g. `0x1234abcd`). If not specified, a random one will be generated.
Expand All @@ -70,7 +72,7 @@ Here's a list of additional environment variables available in this mode:
* DB_USERNAME, DB_PASSWORD - credentials of the user with read and update permissions to the db
* mssql - not supported in docker image yet (feel free to open a PR with that, has to be done in `entrypoint.sh`)
* __`PASSWORD`__: Password to access the stats page. If not set, stats page will not allow accesses.
* __`EMAIL`__: Email address for GDPR requests. Must be specified when telemetry is enabled.
* __`GDPR_EMAIL`__: Email address displayed in the privacy policy for data deletion requests. If not set, the default placeholder text will be shown. This should be set to comply with GDPR requirements when running in production. Must be specified when telemetry is enabled.
* __`DISABLE_IPINFO`__: If set to `true`, ISP info and distance will not be fetched from either [ipinfo.io](https://ipinfo.io) or the offline database. Default: value: `false`
* __`IPINFO_APIKEY`__: API key for [ipinfo.io](https://ipinfo.io). Optional, but required if you want to use the full [ipinfo.io](https://ipinfo.io) APIs (required for distance measurement)
* __`DISTANCE`__: When `DISABLE_IPINFO` is set to false, this specifies how the distance from the server is measured. Can be either `km` for kilometers, `mi` for miles, or an empty string to disable distance measurement. Requires an [ipinfo.io](https://ipinfo.io) API key. Default value: `km`
Expand Down Expand Up @@ -150,6 +152,12 @@ The test can be accessed on port 80.

The list of environment variables available in this mode is the same as [above in standalone mode](#standalone-mode).

If you want the Docker frontend to load its server list from another URL instead of `/servers.json`, set `SERVER_LIST_URL`:

```shell
docker run -e MODE=frontend -e SERVER_LIST_URL="https://example.com/custom-server-list.json" -p 80:8080 -it ghcr.io/librespeed/speedtest
```

#### Example Frontend mode

This command starts LibreSpeed in frontend mode, with a given `servers.json` file, and with telemetry, ID obfuscation, and a stats password and a persistant sqlite database for results:
Expand Down
Loading