Remote FTP For Xenforo Plugin user guide

lutish

Administrator
Staff member

Lutish RemoteFtp — User Guide​

Lutish RemoteFtp moves XenForo attachments to your own FTP / SFTP servers. Files are served from your CDN, sensitive types go through XF permission checks, and your web server stays lean.

Quick Start​

  1. Add a server— Admin → FTP Servers → Add Server
    • Fill in host, credentials, remote path, and which extensions this server handles.
    • Secure Link Extensions — list extensions that need permission enforcement and signed URLs (e.g. rar, zip, 7z).
    • Save → click Test Connection.
  2. Done. New uploads now route to your FTP server automatically.

Transfer Modes​

ModeBehavior
Async (default)Upload finishes instantly. File transfers to FTP in a background queue.
SyncUser waits for the FTP transfer to complete before upload succeeds.

Secure Link (Optional)​

For NGINX secure_link_md5 — set a shared secret per server and list extensions that need signed URLs. Attachments with those extensions:
  1. Show as XF-native links (/attachments/file.zip.123/)
  2. XF checks permissions (daily limits, forum access)
  3. On pass → 301 redirect to CDN with ?sgn=…&ex=… token (72h expiry)
All other extensions serve direct CDN URLs with zero PHP overhead.

Queue Management​

Background jobs run automatically on page loads. To force processing:
Code:
php cmd.php xf:run-jobs
Queue stats are visible under Admin → RemoteFtp → Error Log.

Common Tasks​

TaskWhere
Add / edit FTP serversAdmin → FTP Servers
Test connectionServer edit page → Test Connection
View migration & upload errorsAdmin → RemoteFtp → Error Log
Migrate BD attachmentsCLI: lutish:remotestorage-sync
Migrate local files to FTPCLI: lutish:local-to-ftp
Toggle sync / async per serverServer edit → Upload Mode
Set secure link per serverServer edit → Secure Link Extensions + Secret

CLI Commands​


Migrate from [bd] Attachment Store​

Code:
php cmd.php lutish:remotestorage-sync [--from=N] [--to=N] [--batch-size=N] [--dry-run]
Single-process synchronous migration — no HTTP timeouts, no job queue.

OptionDescription
--from=NStart data_id (inclusive). Default: first unsynced record.
--to=NEnd data_id (inclusive). Default: last unsynced record.
--batch-size=NRecords per batch. Default: 100.
--dry-runPreview only — no changes made.

Code:
# Migrate everything
php cmd.php lutish:remotestorage-sync
# Custom range
php cmd.php lutish:remotestorage-sync --from=1000 --to=5000
# Preview
php cmd.php lutish:remotestorage-sync --dry-run

Migrate Local Attachments to FTP​

Code:
php cmd.php lutish:local-to-ftp --server-id=N [--from=N] [--to=N] [--batch-size=N] [--force] [--dry-run]
Transfers actual files from local disk to FTP and updates database links. Automatically detects and corrects extension mismatches on re-run (e.g. XF Optimization renaming .jpg.webp).

OptionDescription
--server-id=NTarget FTP server ID (required).
--from=NStart data_id (inclusive). Default: first unsynced.
--to=NEnd data_id (inclusive). Default: last unsynced.
--batch-size=NRecords per batch. Default: 50.
--forceRe-upload even if already migrated (use after clearing FTP files).
--dry-runPreview only — no uploads, no DB changes.

Code:
# Migrate to server #1
php cmd.php lutish:local-to-ftp --server-id=1
# Force re-upload
php cmd.php lutish:local-to-ftp --server-id=1 --force

Debug Upload Routing​

Code:
php cmd.php lutish-remoteftp:debug-upload [--extension=ext]
Shows which server would handle a given file extension, the upload mode, and queue status. Useful for verifying server assignment rules.

Code:
# Test default extension (jpg)
php cmd.php lutish-remoteftp:debug-upload
# Test a specific extension
php cmd.php lutish-remoteftp:debug-upload --extension=webp
php cmd.php lutish-remoteftp:debug-upload -e zip

NGINX Secure Link Configuration​

When using Secure Link extensions, configure NGINX to validate signed URLs:

Basic Configuration​

Code:
location / {
    secure_link $arg_sgn,$arg_ex;
    secure_link_md5 "YOUR_SECRET$uri$arg_ex";
    if ($secure_link = "") { return 403; }
    if ($secure_link = "0") { return 410; }
}

With IP Binding​

Code:
secure_link_md5 "YOUR_SECRET$uri$arg_ex$remote_addr";

Cloudflare / Reverse Proxy​

Code:
set $real_ip $http_x_forwarded_for;
if ($real_ip = "") { set $real_ip $remote_addr; }
secure_link $arg_sgn,$arg_ex;
secure_link_md5 "YOUR_SECRET$uri$arg_ex$real_ip";
Match the parameter names (sgn, ex) with your admin settings — these are configurable per server.
 
Back
Top