foreach (Tournament::all() as $tournament) {
                        $url = new SitemapUrl();
-                       $url->path = '/tournaments/'.$tournament->id;
+                       if ($tournament->name) {
+                               $url->path = '/tournaments/'.$tournament->name;
+                       } else {
+                               $url->path = '/tournaments/'.$tournament->id;
+                       }
                        $url->lastmod = $tournament->updated_at ? $tournament->updated_at : ($tournament->created_at ? $tournament->created_at : now());
                        $url->changefreq = $tournament->locked ? 'never' : 'daily';
                        $url->priority = $tournament->locked ? 0.5 : 1.0;
 
                return $tournament->toJson();
        }
 
-       public function single(Request $request, $id) {
-               $tournament = Tournament::with(
+       public function single(Request $request, Tournament $tournament) {
+               $this->authorize('view', $tournament);
+               $tournament->load([
                        'applications',
                        'applications.user',
                        'description',
                        'participants',
                        'participants.user',
-               )->findOrFail($id);
-               $this->authorize('view', $tournament);
+               ]);
                $rounds = $tournament->rounds()->with(['results', 'results.user'])->limit(25)->get();
                foreach ($rounds as $round) {
                        if (!Gate::allows('seeResults', $round)) {
                return $tournament->toJson();
        }
 
+       public function web(Request $request, Tournament $tournament) {
+               $view = view('app')
+                       ->with('title', $tournament->getTranslatedTitle())
+                       ->with('description', $tournament->getTranslatedShort());
+               return $view;
+       }
+
 }
 
 use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 
-class Tournament extends Model
-{
+class Tournament extends Model {
+
        use HasFactory;
 
+       public function resolveRouteBinding(mixed $value, $field = null): Tournament|null {
+               if (is_null($field) && !is_numeric($value)) {
+                       return $this->where('name', '=', $value)->firstOrFail();
+               }
+               return parent::resolveRouteBinding($value, $field);
+       }
+
 
        public function getRunners() {
                $runners = [];
                return $this->hasMany(Round::class)->orderBy('number', 'DESC');
        }
 
+       public function getTranslatedTitle(): string {
+               if ($this->description) {
+                       return $this->description->getTranslatedProperty('title');
+               }
+               return $this->title;
+       }
+
+       public function getTranslatedShort(): string {
+               if ($this->description) {
+                       return $this->description->getTranslatedProperty('short');
+               }
+               return '';
+       }
+
 
        protected $casts = [
                'accept_applications' => 'boolean',
 
--- /dev/null
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+       /**
+        * Run the migrations.
+        */
+       public function up(): void {
+               Schema::table('tournaments', function (Blueprint $table) {
+                       $table->string('name')->nullable()->default(null)->after('id')->unique();
+               });
+       }
+
+       /**
+        * Reverse the migrations.
+        */
+       public function down(): void {
+               Schema::table('tournaments', function (Blueprint $table) {
+                       $table->dropColumn('name');
+               });
+       }
+};
 
                                content={getTranslation(tournament.description, 'short', i18n.language)}
                        />
                </Helmet> : null}
-               <CanonicalLinks base={`/tournaments/${tournament.id}`} />
+               <CanonicalLinks base={`/tournaments/${tournament.name || tournament.id}`} />
                <Detail
                        actions={actions}
                        tournament={tournament}
 
 Route::get('tech', 'App\Http\Controllers\TechniqueController@search');
 Route::get('tech/{tech:name}', 'App\Http\Controllers\TechniqueController@single');
 
-Route::get('tournaments/{id}', 'App\Http\Controllers\TournamentController@single');
+Route::get('tournaments/{tournament}', 'App\Http\Controllers\TournamentController@single');
 Route::get('tournaments/{tournament}/more-rounds', 'App\Http\Controllers\TournamentController@moreRounds');
 Route::post('tournaments/{tournament}/apply', 'App\Http\Controllers\TournamentController@apply');
 Route::post('tournaments/{tournament}/close', 'App\Http\Controllers\TournamentController@close');
 
        return app()->call('App\Http\Controllers\TechniqueController@web', ['type' => 'tech', 'name' => $name]);
 });
 
+Route::get('/tournaments/{tournament}', 'App\Http\Controllers\TournamentController@web');
+
 Route::get('/twitch/guessing-game-leaderboard/{channel:twitch_id}/{type}', 'App\Http\Controllers\ChannelController@getGuessingGameLeaderboard');
 
 Route::view('/{path?}', 'app')->where('path', '.*');