SagerNet / SagerNet/sing-box

Remote rule-set cache is not invalidated when URL changes with the same tag

Open
#4,435 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
38.1k
Forks
4.6k
Avg merge
19d 15h
Merged PRs (30d)
1

Description

Operating system

Windows

System version

1.14.0-beta.17

Installation type

sing-box for Windows Graphical Client

If you are using a graphical client, please provide the version of the client.

1.14.0-beta.17

Version

Description

When a remote rule-set URL is changed while keeping the same tag, sing-box may continue using the previously cached rule-set instead of fetching the new URL immediately.

This happens even when both the old and new rule-set use the same format, for example source JSON → source JSON.

Because the cached rule-set is still valid and can be parsed successfully, sing-box restores it from cache and keeps using its previous LastUpdated value. The newly configured URL may therefore not be fetched until update_interval expires.

This can cause the active rule-set to be different from the rule-set configured in the current configuration file, without any obvious error.

Reproduction

Initial configuration:

{
"type": "remote",
"tag": "example",
"format": "source",
"url": "https://example.com/rules-a.json",
"update_interval": "24h"
}

Start sing-box and allow the rule-set to be downloaded and cached.

Then change only the URL:

{
"type": "remote",
"tag": "example",
"format": "source",
"url": "https://example.com/rules-b.json",
"update_interval": "24h"
}

The tag remains unchanged.

Reload or restart sing-box.

Actual behavior

sing-box restores the cached rule-set associated with:

example

and continues using the content previously downloaded from:

https://example.com/rules-a.json

Because the cached rule-set is valid, its previous LastUpdated value is restored.

If:

LastUpdated + update_interval > current time

the new URL:

https://example.com/rules-b.json

is not fetched immediately.

As a result, the configuration contains URL B, but the active rule-set may still contain the rules downloaded from URL A.

No warning is shown indicating that the cached rule-set belongs to a different URL.

Expected behavior

When the configured URL of a remote rule-set changes, the cache entry should be considered stale even if the tag remains unchanged.

sing-box should immediately fetch the rule-set from the newly configured URL.

At minimum, the cache identity should include or persist the remote URL so that sing-box can detect:

cached URL != configured URL

and invalidate the cached content, LastUpdated, and ETag.

Why this is problematic

This issue is particularly difficult to notice when both URLs use the same rule-set format.

For example:

source JSON A

source JSON B

The cached content can still be parsed successfully, so there may be no error or warning.

The user may believe the newly configured rule-set is active while sing-box is actually still using the old cached rule-set until update_interval expires.

This can result in unexpected routing behavior.

Possible cause

The remote rule-set cache appears to be identified primarily by the rule-set tag.

Conceptually:

cache key = tag

instead of something such as:

cache key = tag + URL

or storing the source URL as metadata and invalidating the cache when the URL changes.

Because of this, changing the URL while keeping the same tag can reuse a cache entry created for a completely different remote resource.

The cached ETag may also belong to the previous URL, so it may be safer to clear the cached ETag whenever the configured URL changes.

Suggested fix

Store the source URL together with the cached remote rule-set.

When loading the cache:

if cachedURL != configuredURL:
invalidate cached content
clear LastUpdated
clear ETag
fetch configuredURL immediately

Alternatively, include the URL or a hash of the URL in the cache identity.

Reproduction steps
Enable experimental.cache_file.
Configure a remote source rule-set with:
tag: example
URL A
a relatively long update_interval, such as 24h.
Start sing-box and confirm URL A is downloaded.
Stop or reload sing-box shortly afterwards.
Change the rule-set URL from URL A to URL B.
Keep:
the same tag;
the same format.
Reload/start sing-box.
Observe that URL B is not immediately requested.
Observe that rules from URL A remain active until the scheduled update time.

Logs

Supporter
Integrity requirements
  • I confirm that I have read the documentation, understand the meaning of all the configuration items I wrote, and did not pile up seemingly useful options or default values.
  • I confirm that I have provided the server and client configuration files and process that can be reproduced locally, instead of a complicated client configuration file that has been stripped of sensitive data.
  • I confirm that I have provided the simplest configuration that can be used to reproduce the error I reported, instead of depending on remote servers, TUN, graphical interface clients, or other closed-source software.
  • I confirm that I have provided the complete configuration files and logs, rather than just providing parts I think are useful out of confidence in my own intelligence.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the issue with experimental.cache_file, using the URL A/URL B configuration and a long update_interval. Trace how the remote rule-set cache is loaded and matched when the tag stays the same. Done means changing the URL invalidates the cached content, LastUpdated, and ETag, then fetches the new URL immediately.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.