You need to write your DB password, mail credentials, and JWT key somewhere. But where?
appsettings.json is the first thing that comes to mind. Easy, fast. But there is a big risk: this file goes to Git along with your source code. If you push to GitHub, the passwords are exposed to everyone. In team settings, everyone can see them too.
What about tools like Azure Key Vault, HashiCorp Vault, Secret Manager? These are great solutions — but there is no access on shared hosting. No SSH, no root, no service installation.
The solution is simple but effective: the environmentVariables block inside web.config.
The web.config file lives on the server and does not go to Git. The environment variables you put inside it are automatically added to ASP.NET Core's configuration system and override the same keys in appsettings.json.
<aspNetCore processPath="dotnet" arguments=".\MyApp.dll"
stdoutLogEnabled="false"
stdoutLogFile=".\logs\stdout"
hostingModel="outofprocess">
<environmentVariables>
<!-- DB bağlantısı — şifre burada, appsettings.json'da değil -->
<environmentVariable name="ConnectionStrings__DefaultConnection"
value="Server=.\SQLEXPRESS;Database=MyDb;User Id=sa;Password=Sifre123;TrustServerCertificate=True;" />
<!-- Mail sunucusu ayarları -->
<environmentVariable name="EmailSettings__SmtpServer" value="smtp.brevo.com" />
<environmentVariable name="EmailSettings__Port" value="587" />
<environmentVariable name="EmailSettings__Username" value="kullanici@ornek.com" />
<environmentVariable name="EmailSettings__Password" value="MailSifresi" />
<!-- JWT — private key dosyasının yolu -->
<environmentVariable name="Jwt__PrivateKeyPath" value="private.key" />
</environmentVariables>
</aspNetCore>
A common mistake when writing connection strings: a connection string that works in JSON doesn't work in XML.
The reason is simple: in JSON, backslash (\) is a special character — you need to write \\ to represent it. In XML, backslash is not a special character — you write it as \ directly.
// appsettings.json — JSON'da \\ gerekir
{
"ConnectionStrings": {
"DefaultConnection": "Server=.\\SQLEXPRESS;Database=MyDb;..."
}
}
<!-- web.config — XML'de tek \ yeterli -->
<environmentVariable name="ConnectionStrings__DefaultConnection"
value="Server=.\SQLEXPRESS;Database=MyDb;..." />
When dotnet publish runs, which files are included in the publish output is controlled from the .csproj file.
Two important rules:
Never — these files may contain secrets, they should not go into the publish output.Always — the application needs this file, it must be in the publish output.<!-- .csproj -->
<ItemGroup>
<!-- Sırlar içerebilir — publish'e gitmesin -->
<Content Update="appsettings.Production.json">
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</Content>
<Content Update="appsettings.Development.json">
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</Content>
<!-- JWT imzalamak için gerekli — publish'e gitsin -->
<Content Include="private.key">
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
</Content>
</ItemGroup>
What do you do when the site won't start in production or you get strange errors? No SSH, no Visual Studio debugger. The only solution: logs.
When the stdoutLogEnabled attribute is set to true, the application output is written to the logs/ folder. Find the error, fix it, then set it back to false.
<!-- Normalde: production'da kapalı -->
<aspNetCore stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" ... />
<!-- Hata ayıklarken: aç, hatayı bul, kapat -->
<aspNetCore stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" ... />