هاست و انتشار پروژه ASP.NET؛ از خروجی Publish تا سایت زنده روی اینترنت
پروژهای که فقط روی لپتاپ شما اجرا میشود، هنوز نیمهکاره است. در این راهنما مسیر کامل انتشار یک برنامه ASP.NET Core را میبینید: ساخت خروجی بهینه، انتخاب نوع میزبانی، پیکربندی IIS روی ویندوز و Nginx روی لینوکس، اجرای داکری، تنظیم SSL، مدیریت رشته اتصال و چکلیست نهایی محیط تولید.
انتخاب نوع میزبانی؛ اولین تصمیم مهم
پیش از هر کاری باید بدانید پروژهتان قرار است کجا زندگی کند. چهار گزینه اصلی وجود دارد و انتخاب میان آنها عمدتاً به بودجه، حجم ترافیک و میزان کنترلی که لازم دارید بستگی دارد.
| نوع میزبانی | مزیت | محدودیت | مناسب برای |
|---|---|---|---|
| هاست اشتراکی ویندوز | ارزان و آماده؛ پنل مدیریتی ساده | کنترل کم، منابع محدود، نسخه رانتایم ثابت | سایتهای کوچک و کمترافیک |
| سرور مجازی (VPS) | کنترل کامل، منابع اختصاصی، هزینه متعادل | نیاز به دانش مدیریت سرور | بیشتر پروژههای واقعی |
| کانتینر و داکر | محیط یکسان در توسعه و تولید، مقیاسپذیری آسان | منحنی یادگیری بیشتر | تیمها و پروژههای در حال رشد |
| پلتفرم ابری | مقیاس خودکار، سرویس مدیریتشده | هزینه بالاتر و وابستگی به ارائهدهنده | پروژههای پرترافیک |
توصیه عملی ما برای اکثر پروژههای ایرانی: یک سرور مجازی با منابع متوسط. هزینهاش کمی بیشتر از هاست اشتراکی است اما کنترل کامل به شما میدهد، محدودیت نسخه رانتایم ندارد و وقتی پروژه رشد کرد لازم نیست همهچیز را از نو مهاجرت دهید. اگر هم روی داکر مسلط شوید، جابهجایی میان سرورها تقریباً بیدردسر میشود.
ساخت خروجی با dotnet publish
دستور dotnet run برای توسعه است، نه تولید. برای انتشار باید خروجی بهینه بسازید؛ خروجیای که
فایلهای اضافی، نمادهای اشکالزدایی و ابزار توسعه در آن نیست.
# انتشار وابسته به فریمورک (رانتایم باید روی سرور نصب باشد)
dotnet publish -c Release -o ./publish
# انتشار خوداتکا برای ویندوز ۶۴ بیتی
dotnet publish -c Release -r win-x64 --self-contained true -o ./publish-win
# انتشار خوداتکا برای لینوکس، با تکفایل و کاهش حجم
dotnet publish -c Release -r linux-x64 --self-contained true \
-p:PublishSingleFile=true -p:PublishTrimmed=true -o ./publish-linux
کدام حالت را انتخاب کنم؟
در حالت وابسته به فریمورک، حجم خروجی چند مگابایت است اما رانتایم داتنت باید روی سرور نصب باشد. مزیت آن این است که با نصب یک وصله امنیتی روی سرور، همه برنامهها بهروز میشوند. در حالت خوداتکا، رانتایم همراه برنامه منتشر میشود؛ حجم بیشتر است اما به هیچ پیشنیازی روی سرور وابسته نیستید — گزینهای عالی برای هاستهایی که نسخه دلخواه شما را نصب ندارند.
appsettings.Development.json و هرگونه
کلید یا رمز توسعه در خروجی نهایی وجود ندارد. یک مرور سریع پوشه publish پیش از آپلود، بارها
جلوی فاش شدن اطلاعات حساس را گرفته است.
میزبانی روی ویندوز و IIS
روی ویندوز، IIS نقش پروکسی معکوس را ایفا میکند: درخواست را میگیرد و به فرآیند Kestrel برنامه شما میسپارد. برای این کار باید ASP.NET Core Hosting Bundle روی سرور نصب باشد؛ این بسته هم رانتایم و هم ماژول لازم برای IIS را نصب میکند.
مراحل انتشار
- نصب Hosting Bundle روی سرور و سپس اجرای
iisreset. - ساخت یک وبسایت جدید در IIS و تنظیم مسیر فیزیکی روی پوشه
publish. - در تنظیمات Application Pool، گزینه .NET CLR Version را روی No Managed Code بگذارید. این نکته تعجبآور است اما درست است: برنامه شما خارج از فرآیند IIS اجرا میشود.
- دسترسی خواندن و اجرا برای کاربر استخر برنامه روی پوشه پروژه تنظیم شود.
- دامنه را در بخش Bindings به سایت متصل کنید.
فایل web.config که هنگام انتشار ساخته میشود، معمولاً چنین ساختاری دارد:
<configuration>
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*"
modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet"
arguments=".\MyApp.dll"
stdoutLogEnabled="false"
stdoutLogFile=".\logs\stdout"
hostingModel="inprocess" />
</system.webServer>
</configuration>
اگر برنامه بالا نیامد، مقدار stdoutLogEnabled را موقتاً روی true بگذارید، پوشه
logs را بسازید و پس از یک تلاش ناموفق، فایل لاگ را بخوانید. علت دقیق خطا تقریباً همیشه همانجاست.
پس از رفع مشکل، حتماً دوباره آن را غیرفعال کنید؛ لاگ فعال روی محیط تولید هم کارایی را کم میکند و هم اطلاعات
داخلی برنامه را روی دیسک مینویسد.
میزبانی روی لینوکس با Nginx
این روش امروز رایجترین شکل انتشار پروژههای ASP.NET Core است. الگو ساده است: برنامه شما با Kestrel روی یک پورت محلی گوش میدهد و Nginx ترافیک عمومی را به آن هدایت میکند.
گام ۱ — نصب رانتایم
sudo apt update
sudo apt install -y aspnetcore-runtime-8.0 nginx
گام ۲ — ساخت سرویس systemd
برای اینکه برنامه پس از راهاندازی مجدد سرور یا در صورت خطا خودکار بالا بیاید، آن را بهصورت سرویس تعریف کنید.
فایل /etc/systemd/system/myapp.service:
[Unit]
Description=ASP.NET Core Application
After=network.target
[Service]
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/dotnet /var/www/myapp/MyApp.dll
Restart=always
RestartSec=10
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=ASPNETCORE_URLS=http://localhost:5000
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp
sudo systemctl status myapp
گام ۳ — پیکربندی Nginx بهعنوان پروکسی معکوس
server {
listen 80;
server_name example.com www.example.com;
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
انتشار با داکر
داکر یک مشکل قدیمی را حل میکند: «روی سیستم من که کار میکرد». با کانتینر، برنامه به همراه تمام وابستگیهایش بستهبندی میشود و همان تصویری که روی لپتاپ تست کردهاید، عیناً روی سرور اجرا میشود.
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 8080
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyApp.csproj", "./"]
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENV ASPNETCORE_URLS=http://+:8080
ENTRYPOINT ["dotnet", "MyApp.dll"]
docker build -t myapp:latest .
docker run -d -p 8080:8080 --name myapp \
-e ASPNETCORE_ENVIRONMENT=Production \
-e ConnectionStrings__Default="Server=db;Database=MyDb;User Id=sa;Password=***" \
myapp:latest
توجه کنید که ساخت تصویر در دو مرحله انجام میشود: مرحله اول با SDK کامل کامپایل میکند و مرحله دوم فقط خروجی را روی یک تصویر سبک رانتایم کپی میکند. نتیجه، تصویری بسیار کوچکتر و امنتر است، چون ابزار توسعه و کد منبع در تصویر نهایی وجود ندارد.
پایگاه داده در محیط تولید
انتقال پایگاه داده معمولاً حساسترین بخش انتشار است. سه رویکرد رایج وجود دارد و هرکدام جای خود را دارند.
- اجرای خودکار مهاجرت هنگام راهاندازی: سادهترین راه، اما در محیط تولید ریسک دارد؛ چون اگر چند نمونه از برنامه همزمان بالا بیایند ممکن است تداخل ایجاد شود.
-
تولید اسکریپت SQL و اجرای دستی: با دستور
dotnet ef migrations scriptیک اسکریپت بسازید، آن را بازبینی کنید و روی پایگاه داده اجرا کنید. این روش امنترین گزینه برای محیط تولید است. - اجرا در خط لوله انتشار: در فرآیندهای خودکار، مهاجرت بهعنوان یک گام مجزا و پیش از راهاندازی نسخه جدید اجرا میشود.
و یک قاعده که هیچ استثنایی ندارد: پیش از هر مهاجرت روی محیط تولید، پشتیبان بگیرید. پشتیبان خودکار روزانه را هم فعال کنید و مهمتر از آن، حداقل یکبار بازیابی از پشتیبان را تمرین کنید. پشتیبانی که هرگز آزمایش نشده، یک فرض است نه یک تضمین.
تنظیمات و اسرار محیط تولید
فایل appsettings.json برای مقادیر غیرحساس مناسب است، اما رمزها و کلیدها باید از بیرون تزریق
شوند. در لینوکس این کار با متغیرهای محیطی انجام میشود. توجه کنید که در نام متغیر، دو زیرخط جای نقطه در
ساختار تودرتو مینشیند:
export ASPNETCORE_ENVIRONMENT=Production
export ConnectionStrings__Default="Server=localhost;Database=ShopDb;User Id=app;Password=***"
export Jwt__Key="یک-کلید-تصادفی-و-طولانی"
نکات کلیدی که رعایتشان تفاوت یک انتشار حرفهای و آماتور را میسازد:
- متغیر
ASPNETCORE_ENVIRONMENTحتماً رویProductionباشد؛ در غیر این صورت صفحه خطای توسعهدهنده فعال میماند و جزئیات داخلی برنامه فاش میشود. - کاربر پایگاه داده باید حداقل دسترسی لازم را داشته باشد، نه دسترسی مدیر کل.
- سطح لاگ در محیط تولید روی
WarningیاInformationتنظیم شود، نهDebug. - فایلهای پیکربندی حساس هرگز نباید در مخزن Git باشند؛
.gitignoreرا جدی بگیرید. - کلیدهای رمزنگاری را در جایی پایدار نگه دارید تا با هر بار راهاندازی مجدد، نشست کاربران باطل نشود.
گواهی SSL و انتقال کامل به HTTPS
امروز HTTPS اختیاری نیست. مرورگرها سایت بدون گواهی را ناامن علامت میزنند، بسیاری از قابلیتهای وب فقط روی اتصال امن کار میکنند و موتورهای جستوجو هم HTTPS را یک سیگنال رتبهبندی میدانند. روی لینوکس، گرفتن گواهی رایگان چند دقیقه بیشتر طول نمیکشد:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-run # آزمایش تمدید خودکار
در سمت برنامه هم هدایت خودکار و HSTS را فعال کنید:
app.UseHttpsRedirection();
app.UseHsts();
پس از فعالسازی، چند نکته سئویی را فراموش نکنید: همه لینکهای داخلی باید به نسخه HTTPS اشاره کنند، نسخه
http باید با کد ۳۰۱ به https هدایت شود، آدرس canonical صفحات باید بهروز شود و
نقشه سایت هم آدرسهای امن را فهرست کند. رها کردن نسخه ناامن در کنار نسخه امن، محتوای تکراری میسازد و
اعتبار صفحه را میان دو آدرس تقسیم میکند.
بهینهسازی سرعت و سئوی فنی پس از انتشار
سرعت سایت مستقیماً روی تجربه کاربر و رتبه آن اثر میگذارد. پس از انتشار، این موارد را فعال کنید:
// فشردهسازی پاسخ
builder.Services.AddResponseCompression(o =>
{
o.EnableForHttps = true;
o.Providers.Add<BrotliCompressionProvider>();
o.Providers.Add<GzipCompressionProvider>();
});
app.UseResponseCompression();
// کش طولانی برای فایلهای ایستا
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
ctx.Context.Response.Headers.CacheControl =
"public,max-age=31536000,immutable";
}
});
سایر اقدامات مؤثر:
- کش خروجی برای صفحاتی که محتوایشان کم تغییر میکند.
- بهینهسازی تصاویر و استفاده از قالبهای مدرن؛ تصاویر معمولاً سنگینترین بخش صفحهاند.
- بارگذاری تنبل برای تصاویر پایین صفحه با ویژگی
loading="lazy". - تعیین صریح ابعاد تصاویر تا از جابهجایی ناگهانی چیدمان جلوگیری شود.
- حذف اسکریپتهای مسدودکننده رندر و بارگذاری غیرهمزمان آنها.
- ساخت
sitemap.xmlوrobots.txtو ثبت سایت در ابزارهای وبمستر. - افزودن داده ساختاریافته برای نمایش بهتر در نتایج جستوجو.
نکتهای که بسیاری از توسعهدهندگان بکاند نادیده میگیرند: پس از تمام بهینهسازیهای سمت سرور، اگر لایه نمایش سنگین و بیساختار باشد، معیارهای تجربه کاربری همچنان ضعیف میمانند. برای همین توصیه میکنیم طراحی و پیادهسازی رابط کاربری را به یک متخصص بسپارید؛ ایران دیزاینر بهعنوان طراح حرفهای وب دقیقاً روی همین حوزه — قالب سبک، رسپانسیو و بهینه برای سئو — کار میکند.
انتشار خودکار؛ از آپلود دستی تا خط لوله CI/CD
در شروع کار، انتشار دستی کاملاً قابل قبول است: خروجی میسازید، فایلها را منتقل میکنید و سرویس را دوباره راه میاندازید. اما بهمحض اینکه انتشار به کاری تکراری تبدیل شود، این روش دو مشکل جدی پیدا میکند. اول خطای انسانی — دیر یا زود کسی نسخه اشتباه را آپلود میکند یا فایل پیکربندی تولید را با نسخه توسعه بازنویسی میکند. دوم اینکه انتشار به یک کار ترسناک تبدیل میشود و تیم آن را به تعویق میاندازد، که یعنی هر بار حجم بزرگتری از تغییرات یکجا منتشر میشود و ریسک بالاتر میرود.
راهحل، خودکارسازی است. یک خط لوله انتشار معمولاً چهار مرحله دارد: با هر تغییر روی شاخه اصلی، کد بازیابی و کامپایل میشود؛ سپس تستها اجرا میشوند و اگر حتی یکی شکست بخورد، فرآیند همانجا متوقف میشود؛ در صورت موفقیت خروجی publish ساخته میشود؛ و در نهایت خروجی روی سرور مستقر میشود و سرویس دوباره راهاندازی میگردد. ابزارهای رایج برای این کار GitHub Actions، GitLab CI و Azure DevOps هستند و برای پروژههای داتنت هر سه پیکربندی سادهای دارند.
دو نکته که تجربه به ما یاد داده است: اول اینکه رمزها و کلیدهای دسترسی سرور باید در بخش اسرار همان ابزار ذخیره شوند، نه در فایل پیکربندی خط لوله که در مخزن قرار میگیرد. دوم اینکه همیشه راهی برای بازگشت سریع به نسخه قبل نگه دارید؛ سادهترین شکل آن این است که نسخه پیشین را روی سرور آرشیو کنید تا در صورت بروز مشکل، بازگرداندن سایت چند ثانیه طول بکشد نه چند ساعت.
مانیتورینگ و سلامت برنامه پس از انتشار
انتشار پایان کار نیست؛ نقطه شروع مرحلهای است که در آن باید بدانید برنامهتان واقعاً چه حالی دارد. کمترین کاری که باید انجام دهید، افزودن یک نقطه پایانی سلامت است تا ابزارهای نظارتی یا حتی یک اسکریپت ساده بتوانند هر چند دقیقه وضعیت برنامه و اتصال آن به پایگاه داده را بسنجند.
builder.Services.AddHealthChecks()
.AddDbContextCheck<AppDbContext>();
app.MapHealthChecks("/health");
در کنار آن، سه چیز را زیر نظر بگیرید. لاگ خطاها را در جایی متمرکز نگه دارید تا لازم نباشد برای دیدن یک استثنا به سرور وصل شوید؛ لاگ ساختاریافته این کار را بسیار آسان میکند. منابع سرور — مصرف حافظه، پردازنده و مهمتر از همه فضای دیسک — را پایش کنید؛ پر شدن دیسک بهخاطر رشد بیمهار لاگها، یکی از رایجترین دلایل از دسترس خارج شدن سایتهای کوچک است. و در نهایت زمان پاسخ مسیرهای پرترافیک را اندازه بگیرید تا افت تدریجی کارایی را پیش از آنکه کاربران شکایت کنند تشخیص دهید.
خطاهای رایج و راهحل آنها
خطای 500.30 — راهاندازی برنامه ناموفق بود
یعنی فرآیند برنامه هنگام بالا آمدن از کار افتاده است. رایجترین علتها: نصب نبودن رانتایم مناسب روی سرور، اشتباه بودن رشته اتصال، یا استثنایی که در زمان ثبت سرویسها پرتاب شده. راهحل: لاگ stdout را موقتاً فعال کنید و متن خطا را بخوانید.
خطای 502 در Nginx
یعنی Nginx نتوانسته به برنامه شما وصل شود. با systemctl status myapp بررسی کنید سرویس در حال
اجراست و مطمئن شوید پورت تعریفشده در proxy_pass دقیقاً همان پورتی است که برنامه روی آن گوش میدهد.
فایلهای ایستا نمایش داده نمیشوند
معمولاً یا app.UseStaticFiles() فراموش شده، یا پس از میانافزارهای دیگری قرار گرفته که زودتر
مسیر را قطع میکنند، یا پوشه wwwroot در خروجی publish کپی نشده است.
خطای دسترسی به پایگاه داده
بررسی کنید فایروال سرور اجازه اتصال میدهد، کاربر پایگاه داده دسترسی لازم را دارد و رشته اتصال محیط تولید — نه توسعه — خوانده میشود. مقدار متغیر محیطی را روی خود سرور چاپ کنید تا مطمئن شوید درست تنظیم شده است.
حروف فارسی درست نمایش داده نمیشود
مطمئن شوید صفحه با <meta charset="utf-8"> شروع میشود، ستونهای متنی در پایگاه داده از نوع
یونیکد هستند و ترتیب مرتبسازی پایگاه داده با زبان فارسی سازگار است. ذخیره فارسی در ستونهای غیریونیکد،
منشأ کلاسیک علامت سؤال بهجای حروف است.
چکلیست نهایی پیش از تحویل پروژه
- متغیر محیط روی
Productionتنظیم شده و صفحه خطای توسعهدهنده غیرفعال است. - هیچ رمز یا کلیدی در مخزن کد و در خروجی publish وجود ندارد.
- گواهی SSL نصب شده و تمام ترافیک به HTTPS هدایت میشود.
- پشتیبانگیری خودکار پایگاه داده فعال است و یکبار بازیابی آزمایش شده.
- صفحات خطای ۴۰۴ و ۵۰۰ سفارشی و کاربرپسند ساخته شدهاند.
- لاگبرداری فعال است و فضای دیسک برای رشد لاگها در نظر گرفته شده.
- فشردهسازی پاسخ و کش فایلهای ایستا فعال است.
- عنوان و توضیحات متا برای همه صفحات مهم نوشته شده است.
- نقشه سایت و فایل robots ساخته و ثبت شدهاند.
- سایت روی موبایل آزمایش شده و نمایش آن درست است.
- سرویس در صورت راهاندازی مجدد سرور، خودکار بالا میآید.
- محدودسازی نرخ روی مسیرهای حساس مثل ورود فعال است.
پرسشهای متداول
برای ASP.NET Core حتماً هاست ویندوز لازم است؟
خیر. ASP.NET Core چندسکویی است و روی لینوکس هم بهخوبی اجرا میشود؛ در عمل بسیاری از تیمها آن را روی لینوکس با Nginx منتشر میکنند. فقط پروژههای قدیمی مبتنی بر ASP.NET Framework به ویندوز و IIS نیاز دارند.
تفاوت انتشار وابسته به فریمورک و خوداتکا چیست؟
در حالت وابسته به فریمورک، رانتایم باید روی سرور نصب باشد و حجم خروجی کم است. در حالت خوداتکا، رانتایم همراه برنامه بستهبندی میشود؛ حجم بیشتر است اما هیچ پیشنیازی روی سرور لازم ندارید.
مهاجرتهای EF Core را چطور روی سرور اجرا کنم؟
امنترین روش، تولید اسکریپت با dotnet ef migrations script، بازبینی آن و اجرای دستی روی
پایگاه داده است. اجرای خودکار هنگام راهاندازی برنامه سادهتر است اما در محیط تولید ریسک بیشتری دارد.
چطور بفهمم مشکل از برنامه است یا از سرور؟
اگر برنامه بهصورت مستقیم روی پورت محلی پاسخ میدهد اما از طریق دامنه نه، مشکل در لایه وبسرور یا شبکه است. اگر خود برنامه هم بالا نمیآید، لاگ سرویس یا فایل stdout پاسخ را به شما میدهد.
پس از انتشار چه کاری برای سئو انجام دهم؟
نقشه سایت را ثبت کنید، عنوان و توضیحات متای همه صفحات را بازبینی کنید، آدرس canonical را تنظیم کنید، سرعت بارگذاری را اندازه بگیرید و مطمئن شوید نسخه موبایل بینقص است. جزئیات محتوایی را هم در صفحه آموزش ASP.NET پوشش دادهایم.
جمعبندی
انتشار پروژه آنقدرها که به نظر میرسد ترسناک نیست؛ فقط مجموعهای از گامهای مشخص است که باید به ترتیب و با دقت انجام شوند: خروجی درست بسازید، میزبانی مناسب انتخاب کنید، وبسرور را بهعنوان پروکسی معکوس تنظیم کنید، اسرار را از کد جدا نگه دارید، SSL را فعال کنید و در پایان چکلیست را مرور کنید. توصیه ما این است که همین فرآیند را روی یک پروژه کوچک تمرین کنید تا وقتی پای پروژه واقعی و مشتری در میان است، با اطمینان عمل کنید.
اگر هنوز پروژهای برای انتشار ندارید، از آموزش سیشارپ شروع کنید و سپس با آموزش ASP.NET Core اولین برنامه واقعی خود را بسازید.