You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

django-storages FTP存储在生产环境无法连接的问题求助

问题:DRF部署在PythonAnywhere时,django-storages FTP连接被拒绝(500错误)

前端基于Angular部署在云主机,后端基于DRF部署在PythonAnywhere,配置django-storages通过FTP将用户上传的文件发送到前端所在服务器。本地开发环境功能正常,但生产环境出现Server Error (500)错误,控制台错误信息如下:

2023-04-04 19:32:00,158: Internal Server Error: /admin/portfolio/developer/add/
Traceback (most recent call last):
  File "/home/ferreromanuel/.virtualenvs/portfolio-virtualenv/lib/python3.10/site-packages/storages/backends/ftp.py", line 91, in _start_connection
    ftp.connect(self._config['host'], self._config['port'])
  File "/usr/local/lib/python3.10/ftplib.py", line 158, in connect
    self.sock = socket.create_connection((self.host, self.port), self.timeout,
  File "/usr/local/lib/python3.10/socket.py", line 845, in create_connection
    raise err
  File "/usr/local/lib/python3.10/socket.py", line 833, in create_connection
    sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused
**NO MATCH**
During handling of the above exception, another exception occurred:
**NO MATCH**
Traceback (most recent call last):

  ...

    raise FTPStorageException(
storages.backends.ftp.FTPStorageException: Connection or login error using data {'active': False, 'path': '/htdocs/assets/media/', 'host': 'ftp.manuelferrero.com.ar', 'user': 'webmaster.manuelferrero.com.ar', 'passwd': '*********', 'port': 21}

settings.py配置如下:

import os
import environ

from pathlib import Path


env = environ.Env()
environ.Env.read_env()


SITE_NAME = 'Manuel Ferrero'

BASE_DIR = Path(__file__).resolve().parent.parent

SECRET_KEY = os.environ.get("SECRET_KEY")

DEBUG = os.environ.get("DEBUG") == "True"


ALLOWED_HOSTS = []


INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "rest_framework",
    "corsheaders",
    "portfolio",
    "contact",
]

CSRF_TRUSTED_ORIGINS = ['http://localhost:4200','https://*.127.0.0.1']

MIDDLEWARE = [
    "corsheaders.middleware.CorsMiddleware",
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

ROOT_URLCONF = "core.urls"

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [os.path.join(BASE_DIR, 'templates')],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

WSGI_APPLICATION = "core.wsgi.application"


DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "db.sqlite3",
    }
}


AUTH_PASSWORD_VALIDATORS = [
    {
        "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
    },
    {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",},
    {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",},
    {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",},
]

LANGUAGE_CODE = "es-AR"

TIME_ZONE = "America/Argentina/Buenos_Aires"

USE_I18N = True

USE_TZ = True

STATIC_URL = "static/"

STATIC_ROOT = os.path.join(BASE_DIR, 'static')

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

# Media files settings
PUBLIC_MEDIA_LOCATION = 'assets/media'

MEDIA_URL = f'https://www.manuelferrero.com.ar/{PUBLIC_MEDIA_LOCATION}/'

# Django-Storages settings
DEFAULT_FILE_STORAGE = 'storages.backends.ftp.FTPStorage'

FTP_STORAGE_LOCATION = os.environ.get('FTP_STORAGE_LOCATION')

# CORS settings
CORS_ALLOW_ALL_ORIGINS = True

# Email settings
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

DEFAULT_FROM_EMAIL = 'no-responder@manuelferrero.com.ar'

DEFAULT_CONTACT_EMAIL = 'contacto@manuelferrero.com.ar'



#! =============================== PRODUCTION SETTINGS ===============================
if not DEBUG:
    ALLOWED_HOSTS = [
        'http://www.manuelferrero.com.ar',
        'https://www.manuelferrero.com.ar',
        'http://ferreromanuel.pythonanywhere.com',
        'https://ferreromanuel.pythonanywhere.com',
        'ferreromanuel.pythonanywhere.com'
    ]

    CSRF_TRUSTED_ORIGINS = [
        'http://www.manuelferrero.com.ar',
        'https://www.manuelferrero.com.ar',
        'http://ferreromanuel.pythonanywhere.com',
        'https://ferreromanuel.pythonanywhere.com',
    ]

    # Email settings
    EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'

    EMAIL_HOST = 'smtp.manuelferrero.com.ar'

    EMAIL_HOST_USER = 'no-responder@manuelferrero.com.ar'

    EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD")

    EMAIL_PORT = 587

    EMAIL_USE_TLS = True

    EMAIL_TIMEOUT = 300 # Sec

    DEFAULT_FROM_EMAIL = 'No responder | MF! <no-responder@manuelferrero.com.ar>'

    DEFAULT_CONTACT_EMAIL = 'Contacto | MF! <contacto@manuelferrero.com.ar>'

    # CORS Policy
    CORS_ALLOW_ALL_ORIGINS = False
    
    CORS_ALLOWED_ORIGINS = []

    # Session & CSRF cookies
    SESSION_COOKIE_SECURE = True
    
    SECURE_BROWSER_XSS_FILTER = True
    
    SECURE_CONTENT_TYPE_NOSNIFF = True
    
    SECURE_HSTS_INCLUDE_SUBDOMAINS = True
    
    SECURE_HSTS_SECONDS = 31536000
    
    SECURE_REDIRECT_EXEMPT = []
    
    SECURE_SSL_REDIRECT = True
    
    SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

解决思路

  • 检查PythonAnywhere出站限制:PythonAnywhere默认禁止出站连接到标准FTP端口21(仅允许连接自身FTP服务),这是最可能的原因。可改用SFTP(端口22),或确认前端主机支持非标准FTP端口,同时查看PythonAnywhere后台的出站规则。
  • 手动测试FTP连接:在PythonAnywhere控制台执行ftp ftp.manuelferrero.com.ar命令,验证是否能正常连接,排除域名解析、网络路由问题。
  • 确认FTP凭据正确性:检查生产环境FTP_STORAGE_LOCATION环境变量包含的主机、用户名、密码、路径是否完全正确,尤其注意密码是否有特殊字符导致解析错误。
  • 切换FTP模式:当前为被动模式(active: False),尝试改为主动模式;或检查前端FTP服务器是否开放被动模式所需的端口范围,是否有防火墙拦截数据流。
  • 替换为SFTPStorage:django-storages支持SFTP后端,安全性更高且PythonAnywhere对SFTP出站限制更少。只需将DEFAULT_FILE_STORAGE改为storages.backends.sftpstorage.SFTPStorage,并配置对应SFTP参数。
  • 检查前端主机防火墙:确认前端服务器的21端口(或SFTP的22端口)对PythonAnywhere的IP地址开放,无防火墙规则拒绝连接。

内容的提问来源于stack exchange,提问作者Manu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 03:07:35