diff --git a/app/controllers/consultation/admin.php b/app/controllers/consultation/admin.php
index 8f354b2c91a851ca00003af9f86b37cc70db0ea9..1eecc29cce6019fdce9142b41610a2993bb21009 100644
--- a/app/controllers/consultation/admin.php
+++ b/app/controllers/consultation/admin.php
@@ -199,6 +199,7 @@ class Consultation_AdminController extends ConsultationController
                 $block->confirmation_text = trim(Request::get('confirmation-text')) ?: null;
                 $block->note              = Request::get('note');
                 $block->size              = Request::int('size', 1);
+                $block->lock_time         = Request::int('lock_time');
 
                 $slots = $block->createSlots(Request::int('duration'), $pause_time, $pause_duration);
                 if (count($slots) === 0) {
@@ -387,6 +388,7 @@ class Consultation_AdminController extends ConsultationController
         $this->block->show_participants = Request::bool('show-participants', false);
         $this->block->require_reason = Request::option('require-reason');
         $this->block->confirmation_text = trim(Request::get('confirmation-text'));
+        $this->block->lock_time = Request::int('lock_time');
 
         foreach ($this->block->slots as $slot) {
             $slot->note = '';
diff --git a/app/views/consultation/admin/create.php b/app/views/consultation/admin/create.php
index 19a34e41c59be86fef91d5fa385817c92bf8de71..31f2b6fec3281de446193e02ddb69a0b945745a6 100644
--- a/app/views/consultation/admin/create.php
+++ b/app/views/consultation/admin/create.php
@@ -140,6 +140,18 @@ $intervals = [
                    value="<?= Request::int('pause_duration', 15) ?>"
                    min="1">
         </label>
+
+        <label>
+            <input type="checkbox" name="lock" value="1" data-shows=".lock-inputs" data-activates=".lock-inputs input">
+            <?= _('Termine für Buchungen sperren?') ?>
+        </label>
+
+        <label class="lock-inputs">
+            <?= _('Wieviele Stunden vor Beginn des Blocks sollen die Termine für Buchungen gesperrt werden?') ?>
+            <input type="number" name="lock_time"
+                   value="<?= htmlReady(Request::int('lock_time', 24)) ?>"
+                   min="1">
+        </label>
     </fieldset>
 
 <? if ($responsible): ?>
diff --git a/app/views/consultation/admin/edit.php b/app/views/consultation/admin/edit.php
index 5f0aec9f18d2891c47decb3bfc01f881b3e82cda..2017c321e8ce2d62f9e2e2f7bccad00ae18df2e4 100644
--- a/app/views/consultation/admin/edit.php
+++ b/app/views/consultation/admin/edit.php
@@ -27,7 +27,20 @@
             <textarea name="note"><?= htmlReady($block->note ) ?></textarea>
         </label>
 
-    <? if ($responsible): ?>
+        <label>
+            <input type="checkbox" name="lock" value="1" data-shows=".lock-inputs" data-activates=".lock-inputs input"
+                   <? if ($block->lock_time) echo 'checked'; ?>>
+            <?= _('Termine für Buchungen sperren?') ?>
+        </label>
+
+        <label class="lock-inputs">
+            <?= _('Wieviele Stunden vor Beginn des Blocks sollen die Termine für Buchungen gesperrt werden?') ?>
+            <input type="number" name="lock_time"
+                   value="<?= htmlReady($block->lock_time) ?>"
+                   min="1">
+        </label>
+
+        <? if ($responsible): ?>
         <?= $this->render_partial('consultation/admin/block-responsibilities.php', compact('responsible', 'block')) ?>
     <? endif; ?>
 
diff --git a/app/views/consultation/admin/index.php b/app/views/consultation/admin/index.php
index 3ad692bfa1583029773222ec82a70fab5b12cb00..52a352cd213a9ce8b725104ef106800e078f09e1 100644
--- a/app/views/consultation/admin/index.php
+++ b/app/views/consultation/admin/index.php
@@ -24,11 +24,11 @@
 <form action="<?= $controller->bulk($page, $current_action === 'expired') ?>" method="post">
 <table class="default consultation-overview">
     <colgroup>
-        <col width="24px">
-        <col width="10%">
-        <col width="12%">
+        <col style="width: 24px">
+        <col style="width: 10%">
+        <col style="width: 12%">
         <col>
-        <col width="48px">
+        <col style="width: 48px">
     </colgroup>
     <thead>
         <tr>
@@ -56,7 +56,7 @@
             <th class="actions">
                 <?= ActionMenu::get()->setContext(strval($block['block']))->addLink(
                     $controller->editURL($block['block'], 0, $page),
-                    _('Bearbeiten'),
+                    _('Block bearbeiten'),
                     Icon::create('edit'),
                     ['data-dialog' => 'size=auto']
                 )->addLink(
diff --git a/app/views/consultation/admin/ungrouped.php b/app/views/consultation/admin/ungrouped.php
index d0a94c03fd76ec819a3511210b7feb0a4ce2cfbf..d41d43dd8fede524f37771d99cc077c0ad2ef38e 100644
--- a/app/views/consultation/admin/ungrouped.php
+++ b/app/views/consultation/admin/ungrouped.php
@@ -62,6 +62,7 @@
                     date('H:i', $block->start),
                     date('H:i', $block->end)
                 ) ?>
+                <?= $this->render_partial('consultation/block-locked.php', compact('block')) ?>
             </td>
             <td>
             <? if (count($block->responsibilities) > 0): ?>
@@ -83,7 +84,7 @@
             <td class="actions">
                 <?= ActionMenu::get()->setContext(strval($block))->addLink(
                     $controller->editURL($block, 0, $page),
-                    _('Information bearbeiten'),
+                    _('Block bearbeiten'),
                     Icon::create('edit'),
                     ['data-dialog' => 'size=auto']
                 )->addLink(
diff --git a/app/views/consultation/block-description.php b/app/views/consultation/block-description.php
index 4d037ff5c4fa862226d3156670eadc3e7518479a..d0b455e1574e97b9fdcb77af6d8a672ad4247c3f 100644
--- a/app/views/consultation/block-description.php
+++ b/app/views/consultation/block-description.php
@@ -6,6 +6,8 @@
     date('H:i', $block->end)
 ) ?>
 
+<?= $this->render_partial('consultation/block-locked.php', compact('block')) ?>
+
 (<?= formatLinks($block->room) ?>)
 
 <? if ($block->show_participants): ?>
diff --git a/app/views/consultation/block-locked.php b/app/views/consultation/block-locked.php
new file mode 100644
index 0000000000000000000000000000000000000000..6d54742a121f7c8f640424552fef144ecde1eb5b
--- /dev/null
+++ b/app/views/consultation/block-locked.php
@@ -0,0 +1,6 @@
+<? if ($block->lock_time): ?>
+    <?= tooltipIcon(sprintf(
+        _('Dieser Block wird %u Stunden vor Beginn für Buchungen gesperrt.'),
+        $block->lock_time
+    )) ?>
+<? endif; ?>
diff --git a/app/views/consultation/overview/index.php b/app/views/consultation/overview/index.php
index c209daadb91799a075ac76fa6894408620cd83bf..dcad6084c7552d20059dcc15b75fecf357ec8682 100644
--- a/app/views/consultation/overview/index.php
+++ b/app/views/consultation/overview/index.php
@@ -1,3 +1,14 @@
+<?php
+/**
+ * @var ConsultationBlock[] $blocks
+ * @var Consultation_OverviewController $controller
+ * @var int $count
+ * @var int $limit
+ * @var int $page
+ *
+ * @var callable $displayNote
+ */
+?>
 <? if (count($blocks) === 0): ?>
 
 <?= MessageBox::info(_('Aktuell werden keine Termine angeboten.'))->hideClose() ?>
@@ -6,10 +17,10 @@
 
 <table class="default">
     <colgroup>
-        <col width="10%">
-        <col width="10%">
+        <col style="width: 10%">
+        <col style="width: 10%">
         <col>
-        <col width="24px">
+        <col style="width: 24px">
     </colgroup>
     <thead>
         <tr>
@@ -45,10 +56,12 @@
                 <a href="<?= $controller->cancel($block, $slot) ?>" data-dialog="size=auto">
                     <?= Icon::create('trash')->asImg(tooltip2(_('Termin absagen'))) ?>
                 </a>
-            <? elseif (!$slot->isOccupied()): ?>
+            <? elseif ($slot->userMayCreateBookingForSlot()): ?>
                 <a href="<?= $controller->book($block, $slot) ?>" data-dialog="size=auto">
                     <?= Icon::create('add')->asImg(tooltip2(_('Termin reservieren'))) ?>
                 </a>
+            <? else: ?>
+                <?= Icon::create('add', Icon::ROLE_INACTIVE)->asImg(tooltip2(_('Dieser Termin ist für Buchungen gesperrt.'))) ?>
             <? endif; ?>
             </td>
         </tr>
diff --git a/app/views/consultation/overview/ungrouped.php b/app/views/consultation/overview/ungrouped.php
index 1a850905b7f2dc1a199a3e1584298235343a2885..b4d5c62b9f03271abc0f6f0a96e4fe8629611cf3 100644
--- a/app/views/consultation/overview/ungrouped.php
+++ b/app/views/consultation/overview/ungrouped.php
@@ -73,12 +73,14 @@
             <td class="actions">
             <? if ($slot->isOccupied($GLOBALS['user']->id)): ?>
                 <a href="<?= $controller->cancel($block, $slot) ?>" data-dialog="size=auto">
-                    <?= Icon::create('remove/consultation')->asImg(tooltip2(_('Termin absagen'))) ?>
+                    <?= Icon::create('trash')->asImg(tooltip2(_('Termin absagen'))) ?>
                 </a>
-            <? elseif (!$slot->isOccupied()): ?>
+            <? elseif ($slot->userMayCreateBookingForSlot()): ?>
                 <a href="<?= $controller->book($block, $slot) ?>" data-dialog="size=auto">
-                    <?= Icon::create('add/consultation')->asImg(tooltip2(_('Termin reservieren'))) ?>
+                    <?= Icon::create('add')->asImg(tooltip2(_('Termin reservieren'))) ?>
                 </a>
+            <? else: ?>
+                <?= Icon::create('add', Icon::ROLE_INACTIVE)->asImg(tooltip2(_('Dieser Termin ist für Buchungen gesperrt.'))) ?>
             <? endif; ?>
             </td>
         </tr>
diff --git a/app/views/consultation/slot-occupation.php b/app/views/consultation/slot-occupation.php
index 656c3ca413df1f7417c8290f902fae077ca4cbe5..035222d2c4bf40d5f64a2d514bfed8ee1155601c 100644
--- a/app/views/consultation/slot-occupation.php
+++ b/app/views/consultation/slot-occupation.php
@@ -1,3 +1,8 @@
+<?php
+/**
+ * @var ConsultationSlot $slot
+ */
+?>
 <? if ($slot->isOccupied()): ?>
     <span class="consultation-occupied">
     <? if ($slot->block->size > 1): ?>
@@ -12,6 +17,10 @@
         <?= _('belegt') ?>
     <? endif; ?>
     </span>
+<? elseif ($slot->isLocked()): ?>
+    <span class="consultation-slot-not-bookable">
+        <?= _('nicht buchbar') ?>
+    </span>
 <? else: ?>
     <span class="consultation-free">
     <? if ($slot->block->size > 1): ?>
diff --git a/db/migrations/5.3.6_add_consultation_lock_time.php b/db/migrations/5.3.6_add_consultation_lock_time.php
new file mode 100644
index 0000000000000000000000000000000000000000..0eba290fac8bca4e692b9287160a6f5ab491c064
--- /dev/null
+++ b/db/migrations/5.3.6_add_consultation_lock_time.php
@@ -0,0 +1,23 @@
+<?php
+final class AddConsultationLockTime extends Migration
+{
+    public function description()
+    {
+        return 'Adds a lock time for consultation blocks that prevents slots '
+             . 'from being booked based on the current time.';
+    }
+
+    protected function up()
+    {
+        $query = "ALTER TABLE `consultation_blocks`
+                  ADD COLUMN `lock_time` INT(11) UNSIGNED DEFAULT NULL AFTER `size`";
+        DBManager::get()->exec($query);
+    }
+
+    protected function down()
+    {
+        $query = "ALTER TABLE `consultation_blocks`
+                  DROP COLUMN `lock_time`";
+        DBManager::get()->exec($query);
+    }
+}
diff --git a/lib/classes/JsonApi/Routes/Consultations/BookingsCreate.php b/lib/classes/JsonApi/Routes/Consultations/BookingsCreate.php
index a7347ab43b4aa9e54aa928971bc918738a4e12e1..dd3566187d0f3bc71b238a933e8fac73f6f162a3 100644
--- a/lib/classes/JsonApi/Routes/Consultations/BookingsCreate.php
+++ b/lib/classes/JsonApi/Routes/Consultations/BookingsCreate.php
@@ -29,6 +29,10 @@ class BookingsCreate extends JsonApiController
             throw new ConflictException('The slot is already occupied');
         }
 
+        if (!$slot->userMayCreateBookingForSlot($booking_user)) {
+            throw new ConflictException('The slot is locked for bookings');
+        }
+
         $booking = \ConsultationBooking::create([
             'slot_id' => $slot->id,
             'user_id' => $booking_user->id,
diff --git a/lib/classes/JsonApi/Schemas/ConsultationBlock.php b/lib/classes/JsonApi/Schemas/ConsultationBlock.php
index 4e4e8d72e1258eb84d129a92a932c31822db61e0..4e3d28c4ea62bf0b1e0a94fd6e9ac2637bb6f97d 100644
--- a/lib/classes/JsonApi/Schemas/ConsultationBlock.php
+++ b/lib/classes/JsonApi/Schemas/ConsultationBlock.php
@@ -26,6 +26,8 @@ class ConsultationBlock extends SchemaProvider
             'start' => date('c', $resource->start),
             'end'   => date('c', $resource->end),
 
+            'lock-time' => $resource->lock_time,
+
             'size'              => (int) $resource->size,
             'show-participants' => (bool) $resource->show_participants,
             'require-reason'    => $resource->require_reason,
diff --git a/lib/classes/JsonApi/Schemas/ConsultationSlot.php b/lib/classes/JsonApi/Schemas/ConsultationSlot.php
index 92678f553224ab8acc3a49f010361c748d0a4620..8452a4a8372900affd56e41ea7057dfdb2cf76cf 100644
--- a/lib/classes/JsonApi/Schemas/ConsultationSlot.php
+++ b/lib/classes/JsonApi/Schemas/ConsultationSlot.php
@@ -17,6 +17,12 @@ class ConsultationSlot extends SchemaProvider
         return $resource->id;
     }
 
+    /**
+     * @param \ConsultationSlot  $resource
+     * @param ContextInterface $context
+     *
+     * @return iterable
+     */
     public function getAttributes($resource, ContextInterface $context): iterable
     {
         $attributes = [
@@ -26,6 +32,9 @@ class ConsultationSlot extends SchemaProvider
             'start_time' => date('c', $resource->start_time),
             'end_time'   => date('c', $resource->end_time),
 
+            'is-bookable' => !$resource->isOccupied() && !$resource->isLocked(),
+            'is-locked'   => $resource->isLocked(),
+
             'mkdate' => date('c', $resource->mkdate),
             'chdate' => date('c', $resource->chdate),
         ];
diff --git a/lib/models/ConsultationBlock.php b/lib/models/ConsultationBlock.php
index 37168ed0fc5bfbff05d6b17a970a651f8c05c30f..c3373e3c1e08576e04daa76b68f924c090fbfb2f 100644
--- a/lib/models/ConsultationBlock.php
+++ b/lib/models/ConsultationBlock.php
@@ -23,10 +23,13 @@
  * @property string $confirmation_text database column
  * @property string $note database column
  * @property string $size database column
+ * @property int $lock_time
  * @property int $mkdate database column
  * @property int $chdate database column
  *
  * @property bool $has_bookings computed column
+ * @property string $range_display
+ * @property bool $is_expired
  * @property Range $range computed column
  * @property ConsultationSlot[]|SimpleORMapCollection $slots has_many ConsultationSlot
  * @property ConsultationResponsibility[]|SimpleCollection $responsibilities has_many ConsultationResponsibility
diff --git a/lib/models/ConsultationSlot.php b/lib/models/ConsultationSlot.php
index f1063ce1161f9c56a67861a621cc6393314a1f48..ab1dfa7ea3fe66d1bbb881cf7c346dfd94548b92 100644
--- a/lib/models/ConsultationSlot.php
+++ b/lib/models/ConsultationSlot.php
@@ -146,6 +146,17 @@ class ConsultationSlot extends SimpleORMap
              : (bool) $this->bookings->findOneBy('user_id', $user_id);
     }
 
+    /**
+     * Returns whether the slot is locked for bookings.
+     *
+     * @return bool
+     */
+    public function isLocked(): bool
+    {
+        return $this->block->lock_time
+            && strtotime("-{$this->block->lock_time} hours", $this->block->start) < time();
+    }
+
     /**
      * Creates a Stud.IP calendar event relating to the slot.
      *
@@ -284,6 +295,18 @@ class ConsultationSlot extends SimpleORMap
         }
     }
 
+    /**
+     * Returns whether the given user may create a booking for this slot.
+     */
+    public function userMayCreateBookingForSlot(\User $user = null): bool
+    {
+        $user = $user ?? User::findCurrent();
+
+        return ConsultationBooking::userMayCreateBookingForRange($this->block->range, $user)
+            && !$this->isOccupied()
+            && !$this->isLocked();
+    }
+
 
     /**
      * @return string A string representation of the consultation slot.
diff --git a/resources/assets/stylesheets/scss/consultation.scss b/resources/assets/stylesheets/scss/consultation.scss
index 8c5bc37ae66c3c267e5eebe186b36e838dfb70d0..246838d877083149200e1924d7c3d30fe26140e5 100644
--- a/resources/assets/stylesheets/scss/consultation.scss
+++ b/resources/assets/stylesheets/scss/consultation.scss
@@ -23,10 +23,13 @@
     }
 }
 .consultation-free {
-    color: $green;
+    color: var(--green);
 }
 .consultation-occupied {
-    color: $red;
+    color: var(--red);
+}
+.consultation-slot-not-bookable {
+    color: var(--light-gray-color);
 }
 
 .consultation-overview {
diff --git a/tests/jsonapi/ConsultationHelper.php b/tests/jsonapi/ConsultationHelper.php
index 820de793e58e367560ee6cf90eec1c785257def5..0fdd40b1b63d7db81801866ae658568b7777f1a4 100644
--- a/tests/jsonapi/ConsultationHelper.php
+++ b/tests/jsonapi/ConsultationHelper.php
@@ -41,19 +41,23 @@ trait ConsultationHelper
         return User::find($credentials['id']);
     }
 
-    protected function createBlockWithSlotsForRange(Range $range): ConsultationBlock
+    protected function createBlockWithSlotsForRange(Range $range, array $additional_data = []): ConsultationBlock
     {
+        $hour = date('H');
+        $begin = strtotime("today {$hour}:00:00");
+        $end = strtotime('+2 hours', $begin);
+
         $blocks = ConsultationBlock::generateBlocks(
             $range,
-            strtotime('today 8:00'),
-            strtotime('today 10:00'),
+            $begin,
+            $end,
             date('w'),
             1
         );
         $blocks = iterator_to_array($blocks);
 
         $block = reset($blocks);
-        $block->setData(self::$BLOCK_DATA);
+        $block->setData(array_merge(self::$BLOCK_DATA, $additional_data));
 
         $block->slots->exchangeArray($block->createSlots(15));
         foreach ($block->slots as $slot) {
diff --git a/tests/jsonapi/ConsultationsBookingCreateBySlotIndexTest.php b/tests/jsonapi/ConsultationsBookingCreateBySlotIndexTest.php
index 2d9be45ee5015f5a3dc30b222ccd450199e9893e..56ef1838ca79f04e53303bca6667003096fcda59 100644
--- a/tests/jsonapi/ConsultationsBookingCreateBySlotIndexTest.php
+++ b/tests/jsonapi/ConsultationsBookingCreateBySlotIndexTest.php
@@ -6,6 +6,7 @@ use WoohooLabs\Yang\JsonApi\Response\JsonApiResponse;
 
 require_once __DIR__ . '/ConsultationHelper.php';
 
+// TODO: Test locked blocks
 class ConsultationsBookingCreateBySlotIndexTest extends Codeception\Test\Unit
 {
     use ConsultationHelper;
@@ -26,6 +27,24 @@ class ConsultationsBookingCreateBySlotIndexTest extends Codeception\Test\Unit
         );
     }
 
+    public function testAutorMayCreateNotCreateBookingDueToLock(): void
+    {
+        $credentials = $this->tester->getCredentialsForTestDozent();
+        $range = User::find($credentials['id']);
+
+        $block = $this->createBlockWithSlotsForRange($range, ['lock_time' => 2]);
+        $slot = $this->getSlotFromBlock($block);
+
+        $response = $this->createBooking(
+            $credentials,
+            $slot,
+            $this->tester->getCredentialsForTestAutor()['id'],
+            null
+        );
+
+        $this->assertEquals(409, $response->getStatusCode());
+    }
+
     public function testSlotIsOccupied(): void
     {
         $credentials = $this->tester->getCredentialsForTestDozent();
diff --git a/tests/jsonapi/ConsultationsBookingCreateTest.php b/tests/jsonapi/ConsultationsBookingCreateTest.php
index e8a85c9aa61849f588e4a58fca6c9006e5f820e8..54ad2b4865bd6985dd3647a108959436deafa71e 100644
--- a/tests/jsonapi/ConsultationsBookingCreateTest.php
+++ b/tests/jsonapi/ConsultationsBookingCreateTest.php
@@ -7,6 +7,7 @@ use WoohooLabs\Yang\JsonApi\Response\JsonApiResponse;
 
 require_once __DIR__ . '/ConsultationHelper.php';
 
+// TODO: Test locked blocks
 class ConsultationsBookingCreateTest extends Codeception\Test\Unit
 {
     use ConsultationHelper;
@@ -27,6 +28,24 @@ class ConsultationsBookingCreateTest extends Codeception\Test\Unit
         );
     }
 
+    public function testAutorMayCreateNotCreateBookingDueToLock(): void
+    {
+        $credentials = $this->tester->getCredentialsForTestDozent();
+        $range = User::find($credentials['id']);
+
+        $block = $this->createBlockWithSlotsForRange($range, ['lock_time' => 2]);
+        $slot = $this->getSlotFromBlock($block);
+
+        $response = $this->createBooking(
+            $credentials,
+            $slot,
+            $this->tester->getCredentialsForTestAutor()['id'],
+            null
+        );
+
+        $this->assertEquals(409, $response->getStatusCode());
+    }
+
     public function testSlotIsOccupied(): void
     {
         $credentials = $this->tester->getCredentialsForTestDozent();