MHSanaei/3x-ui · error

invalid filename

Error message

invalid filename

What it means

ServerController.getDb (database download) builds the attachment filename from serverService.BackupFilename(c.Request.Host) and re-validates it against the package-level filenameRegex before setting Content-Disposition; a mismatch aborts with 400 'invalid filename'. Because the filename is derived from the Host header (and date/db engine), a failure means the Host header produced characters outside the safe pattern — this is a defense against header injection via Host.

Source

Thrown at internal/web/controller/server.go:343

	configJson, err := a.serverService.GetConfigJson()
	if err != nil {
		jsonMsg(c, I18nWeb(c, "pages.index.getConfigError"), err)
		return
	}
	jsonObj(c, configJson, nil)
}

// getDb downloads the database file.
func (a *ServerController) getDb(c *gin.Context) {
	db, err := a.serverService.GetDb()
	if err != nil {
		jsonMsg(c, I18nWeb(c, "pages.index.getDatabaseError"), err)
		return
	}

	filename := a.serverService.BackupFilename(c.Request.Host)
	if !filenameRegex.MatchString(filename) {
		_ = c.AbortWithError(http.StatusBadRequest, fmt.Errorf("invalid filename"))
		return
	}

	c.Header("Content-Type", "application/octet-stream")
	c.Header("Content-Disposition", "attachment; filename="+filename)
	_, _ = c.Writer.Write(db)
}

// getMigration downloads a cross-engine migration file: a .dump on SQLite or a
// .db SQLite database on PostgreSQL, so the data can seed the other backend.
func (a *ServerController) getMigration(c *gin.Context) {
	data, filename, err := a.serverService.GetMigration()
	if err != nil {
		jsonMsg(c, I18nWeb(c, "pages.index.getDatabaseError"), err)
		return
	}
	if !filenameRegex.MatchString(filename) {
		_ = c.AbortWithError(http.StatusBadRequest, fmt.Errorf("invalid filename"))

View on GitHub (pinned to ad32144c42)

Solutions

  1. Send a clean Host header (plain hostname like panel.example.com, no port/underscore) when downloading the DB.
  2. Fix the reverse proxy to set proxy_set_header Host $host without extra decorations.
  3. Check filenameRegex in the controller's file to see exactly which characters are tolerated and adjust the Host accordingly.

Example fix

# before
curl -H 'Host: panel_my.host:443' .../server/getDb   # 400 invalid filename

# after
curl -H 'Host: panel.example.com' .../server/getDb
Defensive patterns

Strategy: validation

Validate before calling

// client side: send a clean Host
await fetch(url, { headers: { Host: 'panel.example.com' } }) // or ensure proxy forwards a clean Host
// server-side harness:
var filenameRe = regexp.MustCompile(`^[A-Za-z0-9._-]+$`)
if !filenameRe.MatchString(host) { /* reject early with clear message */ }

Prevention

When it happens

Trigger: GET the DB backup endpoint with a Host header containing characters outside filenameRegex — underscores in a raw-IP Host, port suffixes, or exotic Unicode from a proxy layer.

Common situations: Accessing the panel through a reverse proxy that forwards an odd Host (e.g. 'panel_my-host' or 'host:443' variants); requesting via curl with a crafted --resolve; older proxies appending ports.

Related errors


AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15). Data as JSON: /api/errors/d55f50188c7b0488. Report an issue: GitHub.